diff --git a/my_deploy.sh b/my_deploy.sh index 7378d4b3bd4315cad23a389cc73fe34ff1f1f5a2..86c1a86f40e5de7e36977a60e5812bdbe21afea9 100644 --- a/my_deploy.sh +++ b/my_deploy.sh @@ -20,7 +20,7 @@ export TFS_REGISTRY_IMAGES="http://localhost:32000/tfs/" # Set the list of components, separated by spaces, you want to build images for, and deploy. -export TFS_COMPONENTS="context device pathcomp service slice nbi webui" +export TFS_COMPONENTS="context device pathcomp service nbi webui" # Uncomment to activate Monitoring (old) #export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring" diff --git a/proto/context.proto b/proto/context.proto index d4253f23fe3789881e23c503715a775c15a692d5..ed194a87d5cf2d2cdc786811bb0e7482333ce121 100644 --- a/proto/context.proto +++ b/proto/context.proto @@ -94,6 +94,10 @@ service ContextService { rpc DeleteOpticalLink (LinkId ) returns (Empty ) {} rpc GetOpticalLinkList (Empty ) returns (OpticalLinkList ) {} + rpc GetOpticalBand (Empty ) returns (OpticalBandList) {} + rpc SelectOpticalBand (OpticalBandId ) returns (OpticalBand) {} + + rpc DeleteServiceConfigRule(ServiceConfigRule) returns (Empty ) {} } @@ -695,6 +699,8 @@ message AuthenticationResult { bool authenticated = 2; } + + // ---------------- Experimental ------------------------ message OpticalConfigId { string opticalconfig_uuid = 1; @@ -715,6 +721,8 @@ message OpticalConfigEvent { } + + // ---- Optical Link ---- message OpticalEndPointId { @@ -730,6 +738,7 @@ message OpticalLinkList { message OpticalLinkDetails { + float length = 1; string src_port = 2; string dst_port = 3; @@ -739,6 +748,7 @@ message OpticalLinkDetails { map c_slots = 7; map l_slots = 8; map s_slots = 9; + } message OpticalLink { @@ -749,6 +759,34 @@ message OpticalLink { } +message ChannelId { + Uuid channel_uuid = 1; +} + +message OpticalBandId { + Uuid opticalband_uuid = 1; +} + + +message OpticalBand { + + OpticalBandId opticalband_id = 1 ; + ConnectionId connection_id =2 ; + ChannelId channel_id = 3; + ServiceId service_id =4; + oneof field { + Service service =5 ; + Connection connection =6 ; + string channel = 7; + } + +} + +message OpticalBandList { + repeated OpticalBand opticalbands =1 ; + +} + ////////////////// Config Rule Delete //////////// diff --git a/src/common/tools/context_queries/OpticalConfig.py b/src/common/tools/context_queries/OpticalConfig.py index fb73cd85f5f81d7142cf6ca914006ec5e2293eee..23d1244cd60a505a329af522509c69f3f5559738 100644 --- a/src/common/tools/context_queries/OpticalConfig.py +++ b/src/common/tools/context_queries/OpticalConfig.py @@ -14,9 +14,11 @@ from common.method_wrappers.ServiceExceptions import InvalidArgumentsException +from context.client.ContextClient import ContextClient +import logging from typing import Optional, Union from uuid import UUID, uuid4, uuid5 - +from common.proto.context_pb2 import OpticalBand, OpticalBandId, Empty # 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: @@ -64,3 +66,34 @@ def opticalconfig_get_uuid( raise InvalidArgumentsException([ ('name', device_name), ], extra_details=['At least one is required to produce a OpticalConfig UUID']) + + +def ob_get_uuid( + ob_name:str , allow_random : bool = False +) -> str: + + if ( ob_name is not None): + + + result = get_uuid_from_string(f'ob_{ob_name}') + + return result + if allow_random: return get_uuid_random() + + raise InvalidArgumentsException([ + ('ob_name ', ob_name), + ], extra_details=['ob_name is required to produce a Optical band UUID']) + + + +def find_optical_band (ob_index)->OpticalBand: + + op_uuid = ob_get_uuid(ob_index) + op_id=OpticalBandId() + op_id.opticalband_uuid.uuid =op_uuid + try: + ctxt = ContextClient() + target_ob= ctxt.SelectOpticalBand(op_id) + return target_ob + except Exception as e : + logging.debug(f"error in finding optical band {e}") diff --git a/src/common/tools/object_factory/OpticalLink.py b/src/common/tools/object_factory/OpticalLink.py index ce6e508e8125cb6f8694d2a73ea28636af00e5e0..e2f4f2420f10bbdc2b22d2651bc9c4aff5d43cd4 100644 --- a/src/common/tools/object_factory/OpticalLink.py +++ b/src/common/tools/object_factory/OpticalLink.py @@ -38,3 +38,44 @@ def correct_slot(dic: dict) -> dict: _dict[key]=1 #print(f"result {_dict}") return _dict + + +## To be deleted , needed now for development purpose ## + +def order_list (lst:list[tuple])->list: + if (len(lst)<=1): + return lst + else : + pivot,bit_val =lst[0] + lst_smaller = [] + lst_greater = [] + for element in lst[1:]: + key,val=element + if (key <= pivot): + lst_smaller.append(element) + else : + lst_greater.append(element) + return order_list(lst_smaller) + [(pivot,bit_val)] + order_list(lst_greater) + +def list_to_dict (lst:list[tuple[int,int]])->dict: + dct = dict() + for ele in lst : + key,value = ele + dct[str(key)]=value + return dct + +def order_dict (dct:dict)->dict: + lst = list() + for key,value in sorted(dct.items()): + lst.append((int(key),value)) + ordered_lst= order_list(lst) + if (len(ordered_lst)>0): + return list_to_dict (ordered_lst) + +def order_dict_v1 (dct:dict)->dict: + lst = list() + for key,value in dct.items(): + lst.append((int(key),value)) + ordered_lst= order_list(lst) + if (len(ordered_lst)>0): + return list_to_dict (ordered_lst) diff --git a/src/context/client/ContextClient.py b/src/context/client/ContextClient.py index cb6f9ca3e6ff7ecb91a97520d68972563ec6f265..4fd742cccf29329e574e507a0fc4ffa7f5317eaf 100644 --- a/src/context/client/ContextClient.py +++ b/src/context/client/ContextClient.py @@ -28,6 +28,7 @@ from common.proto.context_pb2 import ( Service, ServiceConfigRule, ServiceEvent, ServiceFilter, ServiceId, ServiceIdList, ServiceList, Slice, SliceEvent, SliceFilter, SliceId, SliceIdList, SliceList, Topology, TopologyDetails, TopologyEvent, TopologyId, TopologyIdList, TopologyList, + OpticalBand, OpticalBandId, OpticalBandList ) from common.proto.context_pb2_grpc import ContextServiceStub from common.proto.context_policy_pb2_grpc import ContextPolicyServiceStub @@ -491,6 +492,28 @@ class ContextClient: LOGGER.debug('DeleteOpticalChannel result: {:s}'.format(grpc_message_to_json_string(response))) return response + + @RETRY_DECORATOR + def GetOpticalBand(self, request : Empty) -> OpticalBandList: + LOGGER.debug('GetOpticalBand request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.GetOpticalBand(request) + LOGGER.debug('GetOpticalBand result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def SelectOpticalBand(self, request : OpticalBandId) -> OpticalBand: + LOGGER.debug('SelectOpticalBand request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.SelectOpticalBand(request) + LOGGER.debug('SelectOpticalBand result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def SetOpticalBand(self,request : OpticalBand) -> Empty: + LOGGER.debug('SetOpticalBand request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.SetOpticalBand(request) + LOGGER.debug('SetOpticalBand 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))) diff --git a/src/context/service/ContextServiceServicerImpl.py b/src/context/service/ContextServiceServicerImpl.py index 2873c598e3e427070fc1f290ae9fcbd226b24166..272a29be99197572b15012ca5009b7460e9d7c54 100644 --- a/src/context/service/ContextServiceServicerImpl.py +++ b/src/context/service/ContextServiceServicerImpl.py @@ -21,16 +21,17 @@ 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, + Service, ServiceConfigRule, ServiceEvent, ServiceFilter, ServiceId, ServiceIdList, ServiceList, Slice, SliceEvent, SliceFilter, SliceId, SliceIdList, SliceList, Topology, TopologyDetails, TopologyEvent, TopologyId, TopologyIdList, TopologyList, OpticalConfigList, OpticalConfigId, OpticalConfig, OpticalLink, OpticalLinkList, - ServiceConfigRule + OpticalBand, OpticalBandId, OpticalBandList ) 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.ConfigRule import delete_config_rule from .database.Connection import ( connection_delete, connection_get, connection_list_ids, connection_list_objs, connection_set ) @@ -61,6 +62,9 @@ from .database.Topology import ( topology_delete, topology_get, topology_get_details, topology_list_ids, topology_list_objs, topology_set ) +from .database.OpticalBand import ( + get_optical_band, set_optical_band, select_optical_band +) from .database.OpticalConfig import ( set_opticalconfig, select_opticalconfig, get_opticalconfig, delete_opticalconfig, update_opticalconfig, delete_opticalchannel @@ -68,7 +72,8 @@ from .database.OpticalConfig import ( 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') @@ -362,6 +367,22 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer def DeleteOpticalChannel(self, request : OpticalConfig, context : grpc.ServicerContext) -> Empty: delete_opticalchannel(self.db_engine, self.messagebroker, request) return Empty() + + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def GetOpticalBand(self, request : Empty, context : grpc.ServicerContext) -> OpticalBandList: + result = get_optical_band(self.db_engine) + return OpticalBandList(opticalbands=result) + + safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def SelectOpticalBand(self, request : OpticalBandId, context : grpc.ServicerContext) -> OpticalBand: + result = select_optical_band(self.db_engine,request ) + return result + + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def SetOpticalBand(self, request : OpticalBand, context : grpc.ServicerContext) -> Empty: + result = set_optical_band(self.db_engine, request) + return Empty() + #--------------------- Experimental Optical Link ------------------- diff --git a/src/context/service/database/OpticalBand.py b/src/context/service/database/OpticalBand.py new file mode 100644 index 0000000000000000000000000000000000000000..08b88d55b1e8023b2c82b9b2e933bd0a231859b8 --- /dev/null +++ b/src/context/service/database/OpticalBand.py @@ -0,0 +1,70 @@ +# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +from sqlalchemy.engine import Engine +from sqlalchemy.orm import Session, selectinload, sessionmaker +from sqlalchemy_cockroachdb import run_transaction +from sqlalchemy.dialects.postgresql import insert +from common.method_wrappers.ServiceExceptions import NotFoundException +from typing import Dict, List +from common.proto.context_pb2 import OpticalBand,OpticalBandId,OpticalBandList +from .models.OpticalConfig.OpticalBandModel import OpticalBandModel + + +LOGGER = logging.getLogger(__name__) + + + +def get_optical_band(db_engine : Engine): + def callback(session:Session): + results = session.query(OpticalBandModel).all() + + return [obj.dump() for obj in results] + obj = run_transaction(sessionmaker(bind=db_engine), callback) + return obj + + +def select_optical_band( db_engine : Engine ,request:OpticalBandId): + ob_uuid = request.opticalband_uuid.uuid + def callback(session : Session) -> OpticalBand: + stmt = session.query(OpticalBandModel) + stmt = stmt.filter_by(ob_uuid=ob_uuid) + obj = stmt.first() + if obj is not None: + + return obj.dump() + return None + result= run_transaction(sessionmaker(bind=db_engine, expire_on_commit=False), callback) + if result is None : + return result + return OpticalBand(**result) + + +def set_optical_band(db_engine : Engine, ob_data ): + + def callback(session : Session) -> List[Dict]: + if len(ob_data) > 0: + stmt = insert(OpticalBandModel).values(ob_data) + stmt = stmt.on_conflict_do_update( + index_elements=[OpticalBandModel.ob_uuid], + set_=dict( + connection_uuid = stmt.excluded.connection_uuid + ) + ) + stmt = stmt.returning(OpticalBandModel.ob_uuid) + ob_id = session.execute(stmt).fetchone() + + ob_id = run_transaction(sessionmaker(bind=db_engine), callback) + return {'ob_id': ob_id} diff --git a/src/context/service/database/OpticalConfig.py b/src/context/service/database/OpticalConfig.py index aef5608b6f5fcf3a6cd18aa1032fd48b042197c8..24a669f04fba3356e5d789fc5a9ccfd9b5f40034 100644 --- a/src/context/service/database/OpticalConfig.py +++ b/src/context/service/database/OpticalConfig.py @@ -12,24 +12,29 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json, logging +import json, logging ,datetime from sqlalchemy.dialects.postgresql import insert from common.message_broker.MessageBroker import MessageBroker from common.DeviceTypes import DeviceTypeEnum +from sqlalchemy import inspect from sqlalchemy.engine import Engine from sqlalchemy.orm import Session, sessionmaker from sqlalchemy_cockroachdb import run_transaction from common.proto.context_pb2 import OpticalConfig, OpticalConfigId, Empty, EventTypeEnum from .models.OpticalConfig.OpticalConfigModel import OpticalConfigModel -from .models.OpticalConfig.TransponderModel import TransponderTypeModel, OpticalChannelModel +from .models.OpticalConfig.TransponderModel import TransponderTypeModel, OpticalChannelModel, TransponderInterfaceModel from .models.OpticalConfig.RoadmModel import RoadmTypeModel, ChannelModel, ORInterfaceModel + + from context.service.database.uuids.OpticalConfig import ( - channel_get_uuid , opticalconfig_get_uuid ,transponder_get_uuid,roadm_get_uuid, - interface_get_uuid + channel_get_uuid , opticalconfig_get_uuid ,transponder_get_uuid,roadm_get_uuid, + interface_get_uuid , ob_get_uuid + ) from .Events import notify_event_opticalconfig - +from .OpticalBand import set_optical_band LOGGER = logging.getLogger(__name__) +now = datetime.datetime.utcnow() def get_opticalconfig(db_engine : Engine): def callback(session:Session): @@ -69,10 +74,38 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig): if config_type == DeviceTypeEnum.OPTICAL_TRANSPONDER._value_: is_transpondre = True transceivers = [] + if channel_namespace is None and 'channel_namespace' in config: channel_namespace=config['channel_namespace'] - if 'transceivers' in config and len(config['transceivers']['transceiver']) > 0: - transceivers = [transceiver for transceiver in config ['transceivers']['transceiver']] + # if 'transceivers' in config and len(config['transceivers']['transceiver']) > 0: + # transceivers = [transceiver for transceiver in config ['transceivers']['transceiver']] + if 'interfaces' in config and len(config['interfaces']) > 0: + for interface in config['interfaces']: + interface_name=interface["name"] if "name" in interface else None + interfaces.append({ + "transponder_uuid" : transponder_get_uuid(device_id), + 'interface_uuid' : interface_get_uuid(interface_name,device_uuid), + "name" : interface_name, + "status" : interface["status"] if "status" in interface else None, + "operation_status" : interface["operation_status"] if "operation_status" in interface else None, + "ifindex" : interface["ifindex"] if "ifindex" in interface else None, + "in_octets" : interface["in_octets"] if "in_octets" in interface else None, + "in_pkts" : interface["in_pkts"] if "in_pkts" in interface else None, + "in_unicast_pkts" : interface["in_unicast_pkts"] if "in_unicast_pkts" in interface else None, + "in_broadcast_pkts" : interface["in_broadcast_pkts"] if "in_broadcast_pkts" in interface else None, + "in_multicast_pkts" : interface["in_multicast_pkts"] if "in_multicast_pkts" in interface else None, + "out_discards" : interface["out_discards"] if "out_discards" in interface else None, + "out_errors" : interface["out_errors"] if "out_errors" in interface else None, + "in_discards" : interface["in_discards"] if "in_discards" in interface else None, + "in_errors" : interface["in_errors"] if "in_errors" in interface else None, + "out_octets" : interface["out_octets"] if "out_octets" in interface else None, + "out_pkts" : interface["out_pkts"] if "out_pkts" in interface else None, + "out_unicast_pkts" : interface["out_unicast_pkts"] if "out_unicast_pkts" in interface else None, + "out_broadcast_pkts" : interface["out_broadcast_pkts"] if "out_broadcast_pkts" in interface else None, + "out_multicast_pkts" : interface["out_multicast_pkts"] if "out_multicast_pkts" in interface else None, + "last_clear" : interface["last_clear"] if "last_clear" in interface else None + }) + if 'channels' in config and len(config['channels']) > 0: #channels = [channel['name']['index'] for channel in config['channels']] for channel_params in config['channels']: @@ -90,7 +123,7 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig): transponder.append({ "transponder_uuid" : transponder_get_uuid(device_id), "transcievers" : transceivers, - "interfaces" : None, + "opticalconfig_uuid": opticalconfig_uuid, }) @@ -209,7 +242,19 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig): ) ) stmt = stmt.returning(OpticalChannelModel.channel_uuid) - opticalChannel_id = session.execute(stmt).fetchone() + channel_id = session.execute(stmt).fetchone() + if (len(interfaces)>0) : + model_columns = inspect(TransponderInterfaceModel).c.keys() + stmt = insert(TransponderInterfaceModel).values(interfaces) + + stmt = stmt.on_conflict_do_update( + index_elements=[TransponderInterfaceModel.interface_uuid ], + set_={field: stmt.excluded[field] for field in model_columns if field != 'interface_uuid' } + + ) + stmt = stmt.returning(TransponderInterfaceModel.interface_uuid) + interface_id = session.execute(stmt).fetchone() + if config_type == DeviceTypeEnum.OPTICAL_ROADM._value_: if len(roadms) > 0: @@ -285,9 +330,12 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): channel_namespace = None OpticalConfig_data = [] config_type = None + optical_bands = [] + interfaces=[] #is_transpondre = False opticalconfig_uuid = opticalconfig_get_uuid(device_id) - + is_optical_band=None + LOGGER.info(f"update_opticalconfig {request}") if request.config : config = json.loads(request.config) @@ -308,6 +356,33 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): if 'transceivers' in config['new_config'] and len(config['new_config']['transceivers']['transceiver']) > 0: transceivers = [transceiver for transceiver in config['new_config'] ['transceivers']['transceiver']] + if 'interfaces' in config['new_config'] and len(config['new_config']['interfaces']) > 0: + for interface in config['new_config']['interfaces']: + interface_name=interface["name"] if "name" in interface else None + interfaces.append({ + "transponder_uuid" : transponder_get_uuid(device_id), + 'interface_uuid' : interface_get_uuid(interface_name,device_uuid), + "name" : interface_name, + "status" : interface["status"] if "status" in interface else None, + "operation_status" : interface["operation_status"] if "operation_status" in interface else None, + "ifindex" : interface["ifindex"] if "ifindex" in interface else None, + "in_octets" : interface["in_octets"] if "in_octets" in interface else None, + "in_pkts" : interface["in_pkts"] if "in_pkts" in interface else None, + "in_unicast_pkts" : interface["in_unicast_pkts"] if "in_unicast_pkts" in interface else None, + "in_broadcast_pkts" : interface["in_broadcast_pkts"] if "in_broadcast_pkts" in interface else None, + "in_multicast_pkts" : interface["in_multicast_pkts"] if "in_multicast_pkts" in interface else None, + "out_discards" : interface["out_discards"] if "out_discards" in interface else None, + "out_errors" : interface["out_errors"] if "out_errors" in interface else None, + "in_discards" : interface["in_discards"] if "in_discards" in interface else None, + "in_errors" : interface["in_errors"] if "in_errors" in interface else None, + "out_octets" : interface["out_octets"] if "out_octets" in interface else None, + "out_pkts" : interface["out_pkts"] if "out_pkts" in interface else None, + "out_unicast_pkts" : interface["out_unicast_pkts"] if "out_unicast_pkts" in interface else None, + "out_broadcast_pkts" : interface["out_broadcast_pkts"] if "out_broadcast_pkts" in interface else None, + "out_multicast_pkts" : interface["out_multicast_pkts"] if "out_multicast_pkts" in interface else None, + "last_clear" : interface["last_clear"] if "last_clear" in interface else None + }) + if 'channels' in config['new_config'] and len(config['new_config']['channels']) > 0: #channels = [channel['name']['index'] for channel in config['channels']] @@ -349,7 +424,7 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): transponder.append({ "transponder_uuid" : transponder_get_uuid(device_id), "transcievers" : transceivers, - "interfaces" : None, + "opticalconfig_uuid": opticalconfig_uuid, }) @@ -357,6 +432,8 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): if channel_namespace is None and 'channel_namespace' in config['new_config']: channel_namespace=config['new_config']['channel_namespace'] if 'is_opticalband' in config and not config['is_opticalband']: + is_optical_band=config['is_opticalband'] + bidir = config['new_config']['bidir'] #channels = [channel['name']['index'] for channel in config['channels']] if 'flow_handled' in config and len(config['flow_handled'])>0 : num = 0 @@ -379,8 +456,11 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): "optical_band_parent" : str( config['new_config']['ob_id']) if 'ob_id' in config['new_config'] else None, "channel_index" : str(channel_index) if channel_index is not None else None }) + if not bidir: break if 'is_opticalband' in config and config['is_opticalband']: + is_optical_band=config['is_opticalband'] #channels = [channel['name']['index'] for channel in config['channels']] + if 'flow_handled' in config and len(config['flow_handled']) > 0: ob_id = config['new_config']['ob_id'] if 'ob_id' in config['new_config'] else None num = 0 @@ -401,6 +481,13 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): "type" : 'optical_band', "channel_index" : str( channel_index) if channel_index is not None else None }) + optical_bands.append ({ + "channel_uuid" : channel_get_uuid(f'optical_bands_{channel_index}',device_uuid), + 'connection_uuid' : config['connection_uuid'], + "ob_uuid" : ob_get_uuid (channel_index), + 'created_at':now + + }) roadms.append({ "roadm_uuid" : roadm_get_uuid(device_id), @@ -438,6 +525,18 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): ) stmt = stmt.returning(TransponderTypeModel.transponder_uuid) transponder_id = session.execute(stmt).fetchone() + if (len(interfaces)>0) : + model_columns = inspect(TransponderInterfaceModel).c.keys() + stmt = insert(TransponderInterfaceModel).values(interfaces) + + stmt = stmt.on_conflict_do_update( + index_elements=[TransponderInterfaceModel.interface_uuid ], + set_={field: stmt.excluded[field] for field in model_columns if field != 'interface_uuid' } + + ) + stmt = stmt.returning(TransponderInterfaceModel.interface_uuid) + interface_id = session.execute(stmt).fetchone() + if len(channels) > 0: stmt = insert(OpticalChannelModel).values(channels) @@ -481,8 +580,10 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): ) stmt = stmt.returning(ChannelModel.channel_uuid) opticalChannel_id = session.execute(stmt).fetchone() - + opticalconfig_id = run_transaction(sessionmaker(bind=db_engine), callback) + if is_optical_band: set_optical_band(db_engine,optical_bands) + return {'opticalconfig_uuid': opticalconfig_id} def select_opticalconfig(db_engine : Engine, request : OpticalConfigId): @@ -517,30 +618,33 @@ def delete_opticalchannel(db_engine : Engine, messagebroker : MessageBroker, req opticalconfig_uuid = request.opticalconfig_id.opticalconfig_uuid channels = [] config_type = None - + if "type" in config : config_type= config["type"] if 'new_config' in config: - if config_type == DeviceTypeEnum.OPTICAL_TRANSPONDER._value_: - for flow in config['flow']: - src,dest = flow - channel_index= src if src is not None and src!='0' else dest - channel_name= f"channel-{channel_index}" - channels.append({ - # "opticalconfig_uuid":opticalconfig_uuid, - "transponder_uuid" : transponder_get_uuid(device_id), - "channel_uuid" : channel_get_uuid(channel_name ,device_uuid), - "channel_name" : channel_name , - "frequency" : 0, - "operational_mode" : None, - "target_output_power": None, - "status" : "DISABLED" - }) - elif config_type == DeviceTypeEnum.OPTICAL_ROADM._value_: - channel_num = 0 - if 'flow' in config : - if 'is_opticalband' in config: - if config['is_opticalband']: + new_cfg = config['new_config'] + flow_id = new_cfg['flow_id'] if 'flow_id' in new_cfg else 0 + if 'flow' in config: + if config_type == DeviceTypeEnum.OPTICAL_TRANSPONDER._value_: + for flow in config['flow']: + src,dest = flow + channel_index= src if src is not None and src!='0' else dest + channel_name= f"channel-{channel_index}" + channels.append({ + # "opticalconfig_uuid":opticalconfig_uuid, + "transponder_uuid" : transponder_get_uuid(device_id), + "channel_uuid" : channel_get_uuid(channel_name ,device_uuid), + "channel_name" : channel_name , + "frequency" : 0, + "operational_mode" : None, + "target_output_power": None, + "status" : "DISABLED" + }) + elif config_type == DeviceTypeEnum.OPTICAL_ROADM._value_: + channel_num = flow_id + + if 'is_opticalband' in config : + if config['is_opticalband']: ob_id = config['new_config']['ob_id'] if 'ob_id' in config['new_config'] else None if len(config['flow']) == 0: channel_index = ob_id + channel_num @@ -552,16 +656,19 @@ def delete_opticalchannel(db_engine : Engine, messagebroker : MessageBroker, req channel_num +=1 channel_name = f'optical_bands_{channel_index}' channels.append(channel_get_uuid(channel_name, device_uuid)) - else: - if config['flow'] == 0: - channel_num = 1 + else : + + if len(config['flow']) == 0: + channel_name = f'media_channel_{channel_num}' channels.append(channel_get_uuid(channel_name, device_uuid)) else: for flow in config['flow']: - channel_num += 1 channel_name = f'media_channel_{channel_num}' channels.append(channel_get_uuid(channel_name, device_uuid)) + channel_num += 1 + + LOGGER.info(f"channels to delete {channels}") def callback(session : Session): all_suceed = [] @@ -575,11 +682,11 @@ def delete_opticalchannel(db_engine : Engine, messagebroker : MessageBroker, req stmt = stmt.on_conflict_do_update( index_elements=[OpticalChannelModel.channel_uuid ], set_=dict( - channel_name= stmt.excluded.channel_name , - frequency = stmt.excluded.frequency, - operational_mode=stmt.excluded.operational_mode, - target_output_power=stmt.excluded.target_output_power, - status=stmt.excluded.status + channel_name = stmt.excluded.channel_name , + frequency = stmt.excluded.frequency, + operational_mode = stmt.excluded.operational_mode, + target_output_power = stmt.excluded.target_output_power, + status = stmt.excluded.status ) ) stmt = stmt.returning(OpticalChannelModel.channel_uuid) diff --git a/src/context/service/database/models/OpticalConfig/OpticalBandModel.py b/src/context/service/database/models/OpticalConfig/OpticalBandModel.py new file mode 100644 index 0000000000000000000000000000000000000000..735869738053644d1d282a6b13fc9f31bc0d88e1 --- /dev/null +++ b/src/context/service/database/models/OpticalConfig/OpticalBandModel.py @@ -0,0 +1,48 @@ +# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json, logging, operator +from sqlalchemy import Column, DateTime, ForeignKey, Integer, CheckConstraint, String +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import relationship +from typing import Dict +from context.service.database.models._Base import _Base + +LOGGER = logging.getLogger(__name__) + +class OpticalBandModel(_Base): + __tablename__ = 'opticalband' + + ob_uuid = Column(UUID(as_uuid=False), primary_key=True) + connection_uuid = Column(ForeignKey('connection.connection_uuid', ondelete='CASCADE'), nullable=False) + channel_uuid = Column(ForeignKey('channel.channel_uuid', ondelete='CASCADE'), nullable=False) + created_at = Column(DateTime, nullable=False) + ob_channel = relationship('ChannelModel') # back_populates='connections' + ob_connection = relationship('ConnectionModel') # lazy='joined', back_populates='connection' + + def dump_id(self) -> Dict: + return {'opticalband_uuid': {'uuid': self.ob_uuid}} + + def dump(self) -> Dict: + return { + 'opticalband_id': self.dump_id(), + 'channel_id' : self.ob_channel.dump_id(), + 'connection_id' : self.ob_connection.dump_id(), + 'service_id' : self.ob_connection.connection_service.dump_id(), + 'channel' : json.dumps(self.ob_channel.dump()), + 'connection' : self.ob_connection.dump(), + "service" : self.ob_connection.connection_service.dump() + + } + diff --git a/src/context/service/database/models/OpticalConfig/RoadmModel.py b/src/context/service/database/models/OpticalConfig/RoadmModel.py index e090b63065b94219a7afe78ffd425b8db940c6e5..fb48b3e4ea0a231007b0fb64621c9854d66b9fea 100644 --- a/src/context/service/database/models/OpticalConfig/RoadmModel.py +++ b/src/context/service/database/models/OpticalConfig/RoadmModel.py @@ -59,7 +59,7 @@ class ChannelModel(_Base): def dump_id (self ): return { - "channel_uuid": self.channel_uuid + "channel_uuid": {"uuid":self.channel_uuid} } def dump(self): diff --git a/src/context/service/database/models/OpticalConfig/TransponderModel.py b/src/context/service/database/models/OpticalConfig/TransponderModel.py index b0292da4395989c72e9e164bdc858854e6fdca52..5b639d6b09c05dcaa5e41ce8c2d90f1328bc3600 100644 --- a/src/context/service/database/models/OpticalConfig/TransponderModel.py +++ b/src/context/service/database/models/OpticalConfig/TransponderModel.py @@ -23,9 +23,10 @@ class TransponderTypeModel (_Base): transponder_uuid = Column(String, primary_key=True) transcievers = Column(ARRAY(String), nullable=True) - interfaces = Column(String, nullable=True) + opticalconfig_uuid = Column(ForeignKey('optical_config.opticalconfig_uuid', ondelete='CASCADE'), index=True, nullable=False) - + + interfaces = relationship("TransponderInterfaceModel") channels = relationship("OpticalChannelModel") opticalconfig = relationship('OpticalConfigModel', back_populates='transponders') @@ -38,7 +39,7 @@ class TransponderTypeModel (_Base): return { "channels" : [channel.dump() for channel in self.channels], "transceivers" : {"transceiver": [transciever for transciever in self.transcievers]}, - "interfaces" : {"interface":json.loads(self.interfaces) if self.interfaces else ''}, + "interfaces" : [interface.dump() for interface in self.interfaces], "trasponder_uuid" : self.dump_id() } @@ -70,3 +71,63 @@ class OpticalChannelModel(_Base): "operational-mode" : self.operational_mode, "status" : self.status, } + + +class TransponderInterfaceModel(_Base): + __tablename__ = 'transponder_interface' + interface_uuid = Column(String, primary_key=True) + + name = Column (String,nullable=True) + status = Column(String , nullable=True) + operation_status = Column(String , nullable=True) + ifindex = Column(Integer, nullable=True) + in_octets = Column(Integer, nullable=True) + in_pkts = Column(Integer, nullable=True) + in_unicast_pkts = Column(Integer, nullable=True) + in_broadcast_pkts = Column(Integer, nullable=True) + in_multicast_pkts = Column(Integer, nullable=True) + out_discards = Column(Integer, nullable=True) + out_errors = Column(Integer, nullable=True) + in_discards = Column(Integer, nullable=True) + in_errors = Column(Integer, nullable=True) + out_octets = Column(Integer, nullable=True) + out_pkts = Column(Integer, nullable=True) + out_unicast_pkts = Column(Integer, nullable=True) + out_broadcast_pkts = Column(Integer, nullable=True) + out_multicast_pkts = Column(Integer, nullable=True) + + + last_clear = Column(Integer, nullable=True) + + + transponder_uuid = Column(ForeignKey('transponder_type.transponder_uuid', ondelete='CASCADE' ),nullable=False) + transponder = relationship('TransponderTypeModel',back_populates='interfaces') + # opticalconfig_uuid = Column(ForeignKey('optical_config.opticalconfig_uuid', ondelete='CASCADE' ), primary_key=True) + # opticalconfig = relationship('OpticalConfigModel', back_populates='channels') + def dump_id (self ): + return { + "interface_uuid":self.interface_uuid + } + + def dump(self): + return { + "name" :self.name, + "status":self.status, + "operation_status":self.operation_status, + "ifindex":self.ifindex, + "in_octets":self.in_octets, + "in_pkts":self.in_pkts, + "in_unicast_pkts":self.in_unicast_pkts, + "in_broadcast_pkts":self.in_broadcast_pkts, + "in_multicast_pkts":self.in_multicast_pkts, + "in_discards":self.in_discards, + "in_errors":self.in_errors, + "out_discards":self.out_discards, + "out_errors":self.out_errors, + "out_octets":self.out_octets, + "out_pkts":self.out_pkts, + "out_unicast_pkts":self.out_unicast_pkts, + "out_broadcast_pkts":self.out_broadcast_pkts, + "out_multicast_pkts":self.out_multicast_pkts, + "last_clear":self.last_clear + } diff --git a/src/context/service/database/uuids/OpticalConfig.py b/src/context/service/database/uuids/OpticalConfig.py index 781609e9153fd06e8f65bcd77f6f0aaa7e93fadc..819658ecde989e82fbaff686facd8a598805f3e1 100644 --- a/src/context/service/database/uuids/OpticalConfig.py +++ b/src/context/service/database/uuids/OpticalConfig.py @@ -15,6 +15,7 @@ from common.method_wrappers.ServiceExceptions import InvalidArgumentsException from common.proto.context_pb2 import DeviceId from ._Builder import get_uuid_from_string, get_uuid_random +import logging def channel_get_uuid( channel_name : str , device_id : str, allow_random : bool = False @@ -73,3 +74,37 @@ def opticalconfig_get_uuid( raise InvalidArgumentsException([ ('DeviceId ', device_id), ], extra_details=['device_id is required to produce a OpticalConfig UUID']) + + +def ob_get_uuid( + ob_name:str , allow_random : bool = False +) -> str: + + if ( ob_name is not None): + + + result = get_uuid_from_string(f'ob_{ob_name}') + logging.info(f'get_ob_uuid {result}') + return result + if allow_random: return get_uuid_random() + + raise InvalidArgumentsException([ + ('ob_name ', ob_name), + ], extra_details=['ob_name is required to produce a Optical band UUID']) + + + + +def interface_get_uuid( + interface_name :str , device_id:str, allow_random : bool = False +) -> str: + + + if len(interface_name) > 0: + return get_uuid_from_string(interface_name) + device_id + if allow_random: return get_uuid_random() + + raise InvalidArgumentsException([ + ('interface uuid', interface_name), + + ], extra_details=['Interface name is required to produce a interface UUID']) diff --git a/src/device/service/DeviceServiceServicerImpl.py b/src/device/service/DeviceServiceServicerImpl.py index 80ec6ae4d859f3d5628c79ace2138df308745ff3..f6523a107e898087ccf0c274d81f6547667fd6bb 100644 --- a/src/device/service/DeviceServiceServicerImpl.py +++ b/src/device/service/DeviceServiceServicerImpl.py @@ -160,6 +160,7 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): # pass if 'new_optical_config' in new_optical_configs and 'opticalconfig' in new_optical_configs["new_optical_config"]: + LOGGER.info(f"set optical device config {new_optical_configs}") context_client.SetOpticalConfig(new_optical_configs["new_optical_config"]['opticalconfig']) device_id = context_client.SetDevice(device) diff --git a/src/device/service/OpenConfigServicer.py b/src/device/service/OpenConfigServicer.py index 14d7cde66c0aeca14e4b7d4828819bfc8c32ee88..437f31924d8753f9b57c40e7e72e3034c0ac6136 100644 --- a/src/device/service/OpenConfigServicer.py +++ b/src/device/service/OpenConfigServicer.py @@ -81,21 +81,27 @@ class OpenConfigServicer(DeviceServiceServicer): is_all_good = True config = json.loads(request.config) results = None - + + LOGGER.info(f"config from service {config}") + try: context_client = ContextClient() device = get_device( context_client, device_uuid, rw_copy=True, include_endpoints=True, include_components=False, include_config_rules=False) + LOGGER.info(f"get_device {device.name}") if device is None: raise NotFoundException('Device', device_uuid, extra_details='loading in ConfigureDevice') resources, conditions = extract_resources(config=config, device=device) - + LOGGER.info(f"resources {resources}") + LOGGER.info(f"conditions {conditions}") driver : _Driver = get_driver(self.driver_instance_cache, device) results = driver.SetConfig(resources=resources,conditions=conditions) + for result in results: - if not result : + if result is not None: is_all_good = False + raise Exception(result) if is_all_good: #driver.GetConfig(resource_keys=[]) @@ -133,7 +139,7 @@ class OpenConfigServicer(DeviceServiceServicer): #TODO: add a control with the NETCONF get #driver.GetConfig(resource_keys=filter_fields) except Exception as e: - LOGGER.info("error in configuring %s",e) + raise Exception("error in configuring %s",e) context_client.close() return Empty() @@ -144,7 +150,7 @@ class OpenConfigServicer(DeviceServiceServicer): resources : list[dict] = [] is_all_good = True config = json.loads(request.config) - + LOGGER.info(f"from disable optical device {config}") try: context_client = ContextClient() device = get_device( @@ -157,19 +163,13 @@ class OpenConfigServicer(DeviceServiceServicer): resources, conditions = extract_resources(config=config, device=device) driver : _Driver = get_driver(self.driver_instance_cache, device) - if 'edit_type' in conditions and conditions['edit_type'] == 'optical-band': - roadm_configuration = driver.GetConfig() - for resource_data in roadm_configuration: - resource_key, resource_value = resource_data - if resource_key.startswith('/opticalconfigs/opticalconfig/'): - roadm_configuration=resource_value["opticalconfig"] - results = driver.DeleteConfig(resources=resources,conditions=conditions,optical_device_configuration=roadm_configuration) + results = driver.DeleteConfig(resources=resources,conditions=conditions) for result in results: - if not result : + if result is not None: is_all_good = False - + raise Exception(result) if is_all_good: - config = json.loads(request.config) + if "new_config" in config : context_client.DeleteOpticalChannel(request) context_client.close() diff --git a/src/device/service/Tools.py b/src/device/service/Tools.py index 335955b4948de3d8ed3d5afa85a525fc1034543e..153a2c105170302faada354a825caafc23cc9cc9 100644 --- a/src/device/service/Tools.py +++ b/src/device/service/Tools.py @@ -586,6 +586,7 @@ def extract_resources(config : dict, device : Device) -> list[list[dict], dict]: resources.append(is_key_existed('administrative-state', keys_dic=config['new_config'])) resources.append(is_key_existed('frequency', keys_dic=config['new_config'])) resources.append(is_key_existed('width', keys_dic=config['new_config'])) + for port in ports_dic["value"]: circuit_pack_dic = is_key_existed('supporting-circuit-pack-name', keys_dic=port) interface_list = is_key_existed('supporting-interface-list', keys_dic=port) @@ -630,8 +631,9 @@ def extract_resources(config : dict, device : Device) -> list[list[dict], dict]: resources.append(is_key_existed('operational-mode', keys_dic=config['new_config'])) resources.append(is_key_existed('line-port', keys_dic=config['new_config'])) resources.append(is_key_existed('status', keys_dic=config['new_config'], key_name_to_use="admin-state")) - resources.append(is_key_existed('band_type', keys_dic=config['new_config'], key_name_to_use='name')) - resources.append(is_key_existed('ob_id', keys_dic=config['new_config'], key_name_to_use='optical-band-parent')) + resources.append(is_key_existed('band_type', keys_dic=config['new_config'], key_name_to_use='name')) + resources.append(is_key_existed('ob_id', keys_dic=config['new_config'], key_name_to_use='optical-band-parent')) + resources.append(is_key_existed('bidir', keys_dic=config['new_config'])) #resources.append(is_key_existed('name', keys_dic=config['new_config'], key_name_to_use='channel_name')) if not is_opticalband: diff --git a/src/device/service/drivers/oc_driver/OCDriver.py b/src/device/service/drivers/oc_driver/OCDriver.py index e9bcf307f9438bd85c253ccb683d33a67f1e8990..c4020eb69173b7316514dd090005f1609ae3eeac 100644 --- a/src/device/service/drivers/oc_driver/OCDriver.py +++ b/src/device/service/drivers/oc_driver/OCDriver.py @@ -28,9 +28,9 @@ from device.service.driver_api._Driver import _Driver from .templates import compose_config, cli_compose_config, ufi_interface, cisco_interface from .templates.VPN.common import seperate_port_config from .templates.VPN.roadms import ( - create_optical_band, disable_media_channel, delete_optical_band, create_media_channel_v2 + create_optical_band, disable_media_channel, delete_optical_band, create_media_channel, ) -from .templates.VPN.transponder import edit_optical_channel, change_optical_channel_status +from .templates.VPN.transponder import edit_optical_channel, change_optical_channel_status, disable_optical_channel from .RetryDecorator import retry from context.client.ContextClient import ContextClient from common.proto.context_pb2 import OpticalConfig @@ -68,7 +68,7 @@ class NetconfSessionHandler: self.__port = int(port) self.__username = settings.get('username') self.__password = settings.get('password') - self.__vendor = settings.get('vendor') + self.__vendor = settings.get('vendor',None) self.__version = settings.get('version', "1") self.__key_filename = settings.get('key_filename') self.__hostkey_verify = settings.get('hostkey_verify', True) @@ -133,8 +133,8 @@ class NetconfSessionHandler: str_respones = str(response) if re.search(r'', str_respones): - return True - return False + return None + return str_respones @RETRY_DECORATOR def locked(self, target): @@ -155,105 +155,122 @@ def edit_config( str_method = 'DeleteConfig' if delete else 'SetConfig' results = [] - logging.info(f"commmit per rule {commit_per_rule}") + str_config_messages=[] - if str_method == 'SetConfig': - if (conditions['edit_type']=='optical-channel'): - #transponder - str_config_messages = edit_optical_channel(resources) - elif (conditions['edit_type']=='optical-band'): - #roadm optical-band - str_config_messages = create_optical_band(resources) - elif (conditions['edit_type']=='network-media-channel'): - commit_per_rule=True - #openroadm network media channel - str_config_messages = network_media_channel_handler(resources) - else : - #roadm media-channel - str_config_messages=create_media_channel_v2(resources) - #Disabling of the Configuration - else: - # Device type is Transponder - if (conditions['edit_type'] == "optical-channel"): - _,ports,_=seperate_port_config(resources) - str_config_messages=change_optical_channel_status(state="DISABLED",ports=ports) - - # Device type is Roadm - elif (conditions['edit_type']=='optical-band'): - str_config_messages=delete_optical_band(resources) - else : - str_config_messages=disable_media_channel(resources) - - for str_config_message in str_config_messages: - # configuration of the received templates - if str_config_message is None: raise UnsupportedResourceKeyException("CONFIG") - result= netconf_handler.edit_config( # configure the device - config=str_config_message, target=target, default_operation=default_operation, - test_option=test_option, error_option=error_option, format=format) - if commit_per_rule: - netconf_handler.commit() # configuration commit - - #results[i] = True - results.append(result) - - if netconf_handler.vendor == "CISCO": - if "L2VSI" in resources[0][1]: + if netconf_handler.vendor is None : + if str_method == 'SetConfig': + if (conditions['edit_type']=='optical-channel'): + #transponder + str_config_messages = edit_optical_channel(resources) + elif (conditions['edit_type']=='optical-band'): + #roadm optical-band + str_config_messages = create_optical_band(resources) + elif (conditions['edit_type']=='network-media-channel'): + commit_per_rule=True + #openroadm network media channel + str_config_messages = network_media_channel_handler(resources) + else : + #roadm media-channel + str_config_messages=create_media_channel(resources) + #Disabling of the Configuration + else: + # Device type is Transponder + if (conditions['edit_type'] == "optical-channel"): + _,ports,_=seperate_port_config(resources) + #str_config_messages=change_optical_channel_status(state="DISABLED",ports=ports) + str_config_messages=disable_optical_channel(ports=ports) + + # Device type is Roadm + elif (conditions['edit_type']=='optical-band'): + str_config_messages=delete_optical_band(resources) + else : + str_config_messages=disable_media_channel(resources) + + logging.info(f"edit_template : {str_config_messages}") + + for str_config_message in str_config_messages: + # configuration of the received templates + if str_config_message is None: raise UnsupportedResourceKeyException("CONFIG") + result= netconf_handler.edit_config( # configure the device + config=str_config_message, target=target, default_operation=default_operation, + test_option=test_option, error_option=error_option, format=format) + if commit_per_rule: + netconf_handler.commit() # configuration commit + + #results[i] = True + results.append(result) + + else : + if netconf_handler.vendor == "CISCO": + if "L2VSI" in resources[0][1]: + #Configure by CLI + logger.warning("CLI Configuration") + cli_compose_config(resources, + delete=delete, + host= netconf_handler._NetconfSessionHandler__address, + user=netconf_handler._NetconfSessionHandler__username, + passw=netconf_handler._NetconfSessionHandler__password) + for i,resource in enumerate(resources): + results.append(True) + else: + logger.warning("CLI Configuration CISCO INTERFACE") + cisco_interface(resources, + delete=delete, + host= netconf_handler._NetconfSessionHandler__address + , user=netconf_handler._NetconfSessionHandler__username, + passw=netconf_handler._NetconfSessionHandler__password) + for i,resource in enumerate(resources): + results.append(True) + elif netconf_handler.vendor == "UFISPACE": #Configure by CLI - logger.warning("CLI Configuration") - cli_compose_config(resources, delete=delete, host= netconf_handler._NetconfSessionHandler__address, user=netconf_handler._NetconfSessionHandler__username, passw=netconf_handler._NetconfSessionHandler__password) + logger.warning("CLI Configuration: {:s}".format(resources)) + ufi_interface(resources, + delete=delete, + host= netconf_handler._NetconfSessionHandler__address, + user=netconf_handler._NetconfSessionHandler__username, + passw=netconf_handler._NetconfSessionHandler__password) for i,resource in enumerate(resources): results.append(True) - else: - logger.warning("CLI Configuration CISCO INTERFACE") - cisco_interface(resources, delete=delete, host= netconf_handler._NetconfSessionHandler__address, user=netconf_handler._NetconfSessionHandler__username, passw=netconf_handler._NetconfSessionHandler__password) + else: for i,resource in enumerate(resources): - results.append(True) - elif netconf_handler.vendor == "UFISPACE": - #Configure by CLI - logger.warning("CLI Configuration: {:s}".format(resources)) - ufi_interface(resources, delete=delete, host= netconf_handler._NetconfSessionHandler__address, user=netconf_handler._NetconfSessionHandler__username, passw=netconf_handler._NetconfSessionHandler__password) - for i,resource in enumerate(resources): - results.append(True) - else: - for i,resource in enumerate(resources): - str_resource_name = 'resources[#{:d}]'.format(i) - try: - logger.debug('[{:s}] resource = {:s}'.format(str_method, str(resource))) - chk_type(str_resource_name, resource, (list, tuple)) - chk_length(str_resource_name, resource, min_length=2, max_length=2) - resource_key,resource_value = resource - chk_string(str_resource_name + '.key', resource_key, allow_empty=False) - str_config_messages = compose_config( # get template for configuration - resource_key, resource_value, delete=delete, vendor=netconf_handler.vendor, message_renderer=netconf_handler.message_renderer) - for str_config_message in str_config_messages: # configuration of the received templates - if str_config_message is None: raise UnsupportedResourceKeyException(resource_key) - logger.debug('[{:s}] str_config_message[{:d}] = {:s}'.format( - str_method, len(str_config_message), str(str_config_message))) - netconf_handler.edit_config( # configure the device - config=str_config_message, target=target, default_operation=default_operation, - test_option=test_option, error_option=error_option, format=format) - if commit_per_rule: - netconf_handler.commit() # configuration commit - if 'table_connections' in resource_key: - time.sleep(5) # CPU usage might exceed critical level after route redistribution, BGP daemon needs time to reload - - #results[i] = True - results.append(True) - except Exception as e: # pylint: disable=broad-except - str_operation = 'preparing' if target == 'candidate' else ('deleting' if delete else 'setting') - msg = '[{:s}] Exception {:s} {:s}: {:s}' - logger.exception(msg.format(str_method, str_operation, str_resource_name, str(resource))) - #results[i] = e # if validation fails, store the exception - results.append(e) - - if not commit_per_rule: - try: - netconf_handler.commit() - except Exception as e: # pylint: disable=broad-except - msg = '[{:s}] Exception committing: {:s}' - str_operation = 'preparing' if target == 'candidate' else ('deleting' if delete else 'setting') - logger.exception(msg.format(str_method, str_operation, str(resources))) - results = [e for _ in resources] # if commit fails, set exception in each resource + str_resource_name = 'resources[#{:d}]'.format(i) + try: + logger.debug('[{:s}] resource = {:s}'.format(str_method, str(resource))) + chk_type(str_resource_name, resource, (list, tuple)) + chk_length(str_resource_name, resource, min_length=2, max_length=2) + resource_key,resource_value = resource + chk_string(str_resource_name + '.key', resource_key, allow_empty=False) + str_config_messages = compose_config( # get template for configuration + resource_key, resource_value, delete=delete, vendor=netconf_handler.vendor, message_renderer=netconf_handler.message_renderer) + for str_config_message in str_config_messages: # configuration of the received templates + if str_config_message is None: raise UnsupportedResourceKeyException(resource_key) + logger.debug('[{:s}] str_config_message[{:d}] = {:s}'.format( + str_method, len(str_config_message), str(str_config_message))) + netconf_handler.edit_config( # configure the device + config=str_config_message, target=target, default_operation=default_operation, + test_option=test_option, error_option=error_option, format=format) + if commit_per_rule: + netconf_handler.commit() # configuration commit + if 'table_connections' in resource_key: + time.sleep(5) # CPU usage might exceed critical level after route redistribution, BGP daemon needs time to reload + + #results[i] = True + results.append(True) + except Exception as e: # pylint: disable=broad-except + str_operation = 'preparing' if target == 'candidate' else ('deleting' if delete else 'setting') + msg = '[{:s}] Exception {:s} {:s}: {:s}' + logger.exception(msg.format(str_method, str_operation, str_resource_name, str(resource))) + #results[i] = e # if validation fails, store the exception + results.append(e) + + if not commit_per_rule: + try: + netconf_handler.commit() + except Exception as e: # pylint: disable=broad-except + msg = '[{:s}] Exception committing: {:s}' + str_operation = 'preparing' if target == 'candidate' else ('deleting' if delete else 'setting') + logger.exception(msg.format(str_method, str_operation, str(resources))) + results = [e for _ in resources] # if commit fails, set exception in each resource return results @@ -317,8 +334,9 @@ class OCDriver(_Driver): extracted_values = transponder_values_extractor( data_xml=xml_data, resource_keys=transponder_filter_fields, dic=config ) - transceivers, optical_channels_params, channel_namespace, endpoints, ports_result = extracted_values + interfaces,optical_channels_params,channel_namespace,endpoints,ports_result=extracted_values oc_values["channels" ] = optical_channels_params + oc_values["interfaces" ] =interfaces oc_values["transceivers" ] = transceivers oc_values["channel_namespace"] = channel_namespace oc_values["endpoints" ] = endpoints @@ -354,6 +372,7 @@ class OCDriver(_Driver): @metered_subclass_method(METRICS_POOL) def SetConfig(self, resources : List[Tuple[str, Any]],conditions:dict) -> List[Union[bool, Exception]]: + logging.info(f'from driver conditions {conditions}') if len(resources) == 0: return [] results = [] with self.__lock: @@ -372,7 +391,7 @@ class OCDriver(_Driver): @metered_subclass_method(METRICS_POOL) def DeleteConfig( self, resources : List[Tuple[str, Any]], conditions : dict, - optical_device_configuration = None + ) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] diff --git a/src/device/service/drivers/oc_driver/templates/VPN/common.py b/src/device/service/drivers/oc_driver/templates/VPN/common.py index 243ee9ce897663cafaee15d9ecdd2ecf19a5e427..07968a9715abadc657cacde4d7d5bdda3fb00cf5 100644 --- a/src/device/service/drivers/oc_driver/templates/VPN/common.py +++ b/src/device/service/drivers/oc_driver/templates/VPN/common.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging + def seperate_port_config(resources:list,unwanted_keys=[])->list[list,dict,str]: config=[] @@ -27,7 +29,7 @@ def seperate_port_config(resources:list,unwanted_keys=[])->list[list,dict,str]: ports[item['resource_key']]=item['value'] if (item['resource_key']=='index' and item['value'] is not None) : index=item['value'] - + logging.info(f"seperate_port_config {ports}") return [config,ports,index] def extract_ports (resources:list): @@ -51,5 +53,5 @@ def filter_config(resources:list,unwanted_keys=[])->list[list,dict,str]: #if (item['resource_key'] == 'destination_port' or item['resource_key'] == 'source_port') and item['value'] is not None: # ports[item['resource_key']]=item['value'] ports = extract_ports(resources=resources) - + logging.info(f"filter_config {ports}") return [config,ports,index] diff --git a/src/device/service/drivers/oc_driver/templates/VPN/roadms.py b/src/device/service/drivers/oc_driver/templates/VPN/roadms.py index 94097cf8fd87a5079adc874eae7893ae592bec7a..f846e59adc7378037409f65c444e1e5ce14879e3 100644 --- a/src/device/service/drivers/oc_driver/templates/VPN/roadms.py +++ b/src/device/service/drivers/oc_driver/templates/VPN/roadms.py @@ -21,130 +21,28 @@ from .common import seperate_port_config ,filter_config -def create_media_channel_old (resources): - optical_band_namespaces="http://flex-scale-project.eu/yang/flex-scale-mg-on" - results=[] - unwanted_keys=['destination_port','source_port','channel_namespace' - ,'frequency','operational-mode','target-output-power', - "admin-state","flow_handled","channel_num"] - config,ports,index= seperate_port_config(resources,unwanted_keys=unwanted_keys) - - doc, tag, text = Doc().tagtext() - #with tag('config'): - with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): - with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): - with tag('media-channels'): - n = 0 - if 'destination_port' in ports: - n = len(ports['destination_port']) - else: - n = len(ports['source_port']) - for i in range(0, n): - #with tag('channel', operation="create"): - with tag('channel'): - with tag('index'):text(str(int(index)+i)) - with tag('config'): - #with tag('index'):text(index) - for resource in config: - - if resource['resource_key'] == "index": - with tag('index'):text(str(int(index)+i)) - elif resource['resource_key']== 'optical-band-parent' : - with tag('optical-band-parent',xmlns=optical_band_namespaces):text(resource['value']) - else: - with tag(resource['resource_key']):text(resource['value']) - if ('destination_port' in ports) and (ports['destination_port'][i] is not None): - with tag('dest'): - with tag('config'): - with tag('port-name'):text(ports['destination_port'][i]) - if ('source_port' in ports) and (ports['source_port'][i] is not None): - with tag('source'): - with tag('config'): - with tag('port-name'):text(ports['source_port'][i]) - - - result = indent( - doc.getvalue(), - indentation = ' '*2, - newline = '' - ) - results.append(result) - return results - - - - -def create_media_channel (resources): - optical_band_namespaces="http://flex-scale-project.eu/yang/flex-scale-mg-on" - results=[] - unwanted_keys=['destination_port','source_port','channel_namespace' - ,'frequency','operational-mode','target-output-power', - "admin-state","handled_flow","channel_num"] - - config,ports,index=filter_config(resources,unwanted_keys) - - doc, tag, text = Doc().tagtext() - #with tag('config'): - with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): - with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): - with tag('media-channels'): - n = 0 - - for flow in ports: - src,dest=flow - #with tag('channel', operation="create"): - with tag('channel'): - with tag('index'):text(str(int(index)+n)) - with tag('config'): - #with tag('index'):text(index) - for resource in config: - - if resource['resource_key'] == "index": - with tag('index'):text(str(int(index)+n)) - elif resource['resource_key']== 'optical-band-parent' : - with tag('optical-band-parent',xmlns=optical_band_namespaces):text(resource['value']) - else: - with tag(resource['resource_key']):text(resource['value']) - if dest is not None and dest != '0': - with tag('dest'): - with tag('config'): - with tag('port-name'):text(dest) - if src is not None and src != '0': - with tag('source'): - with tag('config'): - with tag('port-name'):text(src) - n+=1 - - result = indent( - doc.getvalue(), - indentation = ' '*2, - newline = '' - ) - results.append(result) - return results - - -def create_media_channel_v2 (resources): +def create_media_channel (resources): optical_band_namespaces="http://flex-scale-project.eu/yang/flex-scale-mg-on" results=[] unwanted_keys=['destination_port','source_port','channel_namespace' ,'frequency','operational-mode','target-output-power', - "handled_flow","channel_num"] + "handled_flow","channel_num",'bidir'] + bidir = next(i['value'] for i in resources if i['resource_key'] == 'bidir') config,ports,index=filter_config(resources,unwanted_keys) + logging.info(f'bidir {bidir}') n = 0 + for flow in ports: doc, tag, text = Doc().tagtext() with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): with tag('media-channels'): - - src,dest=flow with tag('channel', operation="create"): #with tag('channel'): @@ -161,8 +59,7 @@ def create_media_channel_v2 (resources): with tag('admin-status'):text(resource['value']) else: with tag(resource['resource_key']):text(resource['value']) - - + if src is not None and src != '0': with tag('source'): with tag('config'): @@ -170,94 +67,37 @@ def create_media_channel_v2 (resources): if dest is not None and dest != '0': with tag('dest'): with tag('config'): - with tag('port-name'):text(dest) - n+=1 - + with tag('port-name'):text(dest) + + n+=1 result = indent( doc.getvalue(), indentation = ' '*2, newline = '' ) results.append(result) + if not bidir : break return results - - - -def create_optical_band_old (resources) : - results =[] - unwanted_keys=['destination_port','source_port','channel_namespace','frequency','optical-band-parent','flow_handled'] - config,ports,index= seperate_port_config(resources,unwanted_keys=unwanted_keys) - - doc, tag, text = Doc().tagtext() - #with tag('config'): - with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): - with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): - with tag('optical-bands',xmlns="http://flex-scale-project.eu/yang/flex-scale-mg-on"): - n = 0 - if 'destination_port' in ports: - n = len(ports['destination_port']) - else: - n = len(ports['source_port']) - for i in range(0, n): - #with tag('optical-band', operation="create"): - with tag('optical-band'): - if index is not None: - with tag('index'):text(str(int(index)+i)) - with tag('config'): - #if index is not None: - # with tag('index'):text(str(int(index)+i)) - for resource in config: - if resource['resource_key'] == "index": - with tag('index'):text(str(int(index)+i)) - else: - with tag(resource['resource_key']):text(resource['value']) - with tag('admin-status'):text('ENABLED') - #with tag('fiber-parent'):text(ports['destination_port'] if 'destination_port' in ports else ports['source_port']) - if ('destination_port' in ports) and (ports['destination_port'][i] is not None): - with tag('dest'): - with tag('config'): - with tag('port-name'):text(ports['destination_port'][i]) - if ('source_port' in ports) and (ports['source_port'][i] is not None): - with tag('source'): - with tag('config'): - with tag('port-name'):text(ports['source_port'][i]) - - - result = indent( - doc.getvalue(), - indentation = ' '*2, - newline = '' - ) - results.append(result) - return results - - def create_optical_band (resources) : results =[] - unwanted_keys=['destination_port','source_port','channel_namespace','frequency','optical-band-parent','handled_flow'] + unwanted_keys=['destination_port','source_port','channel_namespace' + ,'frequency','optical-band-parent' + ,'handled_flow','bidir'] config,ports,index= filter_config(resources,unwanted_keys=unwanted_keys) - - #with tag('config'): n = 0 for flow in ports: doc, tag, text = Doc().tagtext() with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): with tag('optical-bands',xmlns="http://flex-scale-project.eu/yang/flex-scale-mg-on"): - - - #with tag('optical-band', operation="create"): src,dest=flow - with tag('optical-band'): if index is not None: with tag('index'):text(str(int(index)+n)) with tag('config'): - #if index is not None: - # with tag('index'):text(str(int(index)+i)) for resource in config: if resource['resource_key'] == "index": with tag('index'):text(str(int(index)+n)) @@ -274,8 +114,6 @@ def create_optical_band (resources) : with tag('config'): with tag('port-name'):text(src) n +=1 - - result = indent( doc.getvalue(), indentation = ' '*2, @@ -288,31 +126,42 @@ def create_optical_band (resources) : def disable_media_channel (resources): results=[] - unwanted_keys=['destination_port','source_port','channel_namespace','frequency','operational-mode', 'optical-band-parent'] + bidir = next(i['value'] for i in resources if i['resource_key'] == 'bidir') + unwanted_keys=['destination_port','source_port' + ,'channel_namespace','frequency' + ,'operational-mode', 'optical-band-parent' + ,'bidir' + ] config,ports,index= seperate_port_config(resources,unwanted_keys=unwanted_keys) - doc, tag, text = Doc().tagtext() - #with tag('config'): - with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): - with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): - with tag('media-channels'): - with tag("channel",operation="delete"): - with tag('index'):text(str(int(index))) - with tag('config'): - with tag('index'):text(str(int(index))) - - result = indent( - doc.getvalue(), - indentation = ' '*2, - newline = '' - ) - results.append(result) + n = 0 + for flow in ports: + doc, tag, text = Doc().tagtext() + with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): + with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): + with tag('media-channels'): + with tag("channel",operation="delete"): + with tag('index'):text(str(int(index) + n)) + with tag('config'): + with tag('index'):text(str(int(index) + n)) + + n +=1 + result = indent( + doc.getvalue(), + indentation = ' '*2, + newline = '' + ) + results.append(result) + if not bidir: break return results def disable_optical_band (resources:list,state:str): results=[] - unwanted_keys=['destination_port','source_port','channel_namespace','frequency','operational-mode', 'optical-band-parent'] - config,ports,index= seperate_port_config(resources,unwanted_keys=unwanted_keys) + unwanted_keys=['destination_port','source_port' + ,'channel_namespace','frequency' + ,'operational-mode', 'optical-band-parent' + ,"bidir"] + _,_,index= seperate_port_config(resources,unwanted_keys=unwanted_keys) doc, tag, text = Doc().tagtext() #with tag('config'): with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): @@ -330,28 +179,63 @@ def disable_optical_band (resources:list,state:str): newline = '' ) results.append(result) - return results + return results + + + + +def disable_optical_band_v2 (resources:list,state:str): + results=[] + unwanted_keys=['destination_port','source_port','channel_namespace','frequency','operational-mode', 'optical-band-parent'] + _,ports,index= seperate_port_config(resources,unwanted_keys=unwanted_keys) + n = 0 + for flow in ports: + doc, tag, text = Doc().tagtext() + with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): + with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): + with tag('optical-bands',xmlns="http://flex-scale-project.eu/yang/flex-scale-mg-on"): + with tag('optical-band',operation="delete"): + if index is not None: + with tag('index'):text(str(int(index) + n)) + with tag('config'): + with tag('index'):text(str(int(index) + n)) + + n +=1 + result = indent( + doc.getvalue(), + indentation = ' '*2, + newline = '' + ) + results.append(result) + return results + def delete_optical_band (resources:list): results=[] unwanted_keys=['destination_port','source_port','channel_namespace','frequency','operational-mode', 'optical-band-parent'] - config,ports,index= seperate_port_config(resources,unwanted_keys=unwanted_keys) - doc, tag, text = Doc().tagtext() - #with tag('config'): - with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): - with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): - with tag('optical-bands',xmlns="http://flex-scale-project.eu/yang/flex-scale-mg-on"): - with tag('optical-band',operation="delete"): - if index is not None: - with tag('index'):text(index) - with tag('config'): - with tag('index'):text(index) - - result = indent( - doc.getvalue(), - indentation = ' '*2, - newline = '' - ) - results.append(result) - return results + _,ports,index= seperate_port_config(resources,unwanted_keys=unwanted_keys) + n = 0 + for key,v in ports.items(): + if isinstance(v,list): + if len(v)==1 and v[0] is None : continue + else : + if v is None : continue + doc, tag, text = Doc().tagtext() + with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): + with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): + with tag('optical-bands',xmlns="http://flex-scale-project.eu/yang/flex-scale-mg-on"): + with tag('optical-band',operation="delete"): + if index is not None: + with tag('index'):text(str(int(index) + n)) + with tag('config'): + with tag('index'):text(str(int(index) + n)) + + n +=1 + result = indent( + doc.getvalue(), + indentation = ' '*2, + newline = '' + ) + results.append(result) + return results diff --git a/src/device/service/drivers/oc_driver/templates/VPN/transponder.py b/src/device/service/drivers/oc_driver/templates/VPN/transponder.py index f96a6b9ddc9a439b52db7192f25c9501a758c883..d4d4a051a452c903e3c885bc492277f3ed616daf 100644 --- a/src/device/service/drivers/oc_driver/templates/VPN/transponder.py +++ b/src/device/service/drivers/oc_driver/templates/VPN/transponder.py @@ -65,7 +65,8 @@ def add_transceiver (transceiver_name:str): def create_optical_channel(resources:list[dict],ports:list[dict],config:list[dict] ): - #unwanted_keys=['destination_port','source_port','channel_namespace','optical-band-parent','index', 'name','admin-state'] + # unwanted_keys=['bidir','source_port','destination_port','handled_flow','name','channel_namespace','admin-state' + # ,'optical-band-parent'] results =[] data ={} data["channel_namespace"]=next((i["value"] for i in resources if i["resource_key"] == "channel_namespace"), None) @@ -108,6 +109,49 @@ def create_optical_channel(resources:list[dict],ports:list[dict],config:list[dic return results +def disable_optical_channel(ports:list[dict] ): + + # unwanted_keys=['bidir','source_port','destination_port','handled_flow','name','channel_namespace','admin-state' + # ,'optical-band-parent'] + results =[] + port_val = "" + if 'destination_port' in ports and ports['destination_port'][0] is not None: + port_val = ports['destination_port'][0] + else: + port_val = ports['source_port'][0] + + doc, tag, text = Doc().tagtext() + #with tag('config'): + with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): + with tag('components', xmlns="http://openconfig.net/yang/platform"): + with tag('component'): + with tag('name'):text("channel-{}".format(port_val)) + with tag('config'): + with tag('name'):text("channel-{}".format(port_val)) + with tag('optical-channel',xmlns="http://openconfig.net/yang/terminal-device"): + with tag('config'): + with tag('frequency'):text('199000000') + with tag('target-output-power'):text('-40') + with tag('operational-mode'):text('0') + with tag('terminal-device', xmlns="http://openconfig.net/yang/terminal-device"): + with tag('logical-channels'): + with tag('channel'): + with tag('index'):text("{}".format(port_val)) + with tag('config'): + with tag('index'):text("{}".format(port_val)) + with tag('admin-state'):text("DISABLED") + result = indent( + doc.getvalue(), + indentation = ' '*2, + newline = '' + ) + results.append(result) + + + return results + + + def change_optical_channel_status (state:str,ports:list[dict]) : port_val="" if 'destination_port' in ports and ports['destination_port'][0] is not None: @@ -138,10 +182,11 @@ def change_optical_channel_status (state:str,ports:list[dict]) : return results + def edit_optical_channel (resources:list[dict]): unwanted_keys=['destination_port','source_port','channel_namespace' - ,'optical-band-parent','index', 'name','admin-state','handled_flow'] + ,'optical-band-parent','index', 'name','admin-state','handled_flow','bidir'] config,ports,index=seperate_port_config(resources,unwanted_keys=unwanted_keys) results = [] # channel_name=next((i["value"] for i in resources if i["resource_key"]=="channel_name" and i["value"] != None),None) diff --git a/src/device/service/drivers/oc_driver/templates/discovery_tool/transponders.py b/src/device/service/drivers/oc_driver/templates/discovery_tool/transponders.py index 249b3340ec82c390298fafc80bece61a6007d891..a0b663bcf01c03a56ffa853d1a2f454c1de041c0 100644 --- a/src/device/service/drivers/oc_driver/templates/discovery_tool/transponders.py +++ b/src/device/service/drivers/oc_driver/templates/discovery_tool/transponders.py @@ -107,8 +107,11 @@ def extract_channels_based_on_channelnamespace (xml_data:str,channel_namespace:s # Extract and print the component names for component in component_names: - component_name = component.find('namespace:name', namespace).text - channels.append({"index":component_name}) + component_name = component.find('namespace:name', namespace).text + #fix for ofc2025 demo with HHI transceiver + #if "transceiver" not in component_name and "port" not in component_name: + if "channel" in component_name: + channels.append({"index":component_name}) else : namespaces = { 'wr': 'http://openconfig.net/yang/wavelength-router', @@ -204,28 +207,87 @@ def extract_tranceiver (data_xml:str,dic:dict): dic['transceiver']=component_names return dic -def extract_interface (xml_data:str,dic:dict): + +def extract_interfaces (xml_data:str): + xml_bytes = xml_data.encode("utf-8") root = ET.fromstring(xml_bytes) - namespaces = { - 'ns': 'urn:ietf:params:xml:ns:netconf:base:1.0', - 'oc': 'http://openconfig.net/yang/interfaces', - } - ip_namespaces = { - 'oc': 'http://openconfig.net/yang/interfaces', - 'ip': 'http://openconfig.net/yang/interfaces/ip', - } - interfaces = root.findall('.//oc:interfaces/oc:interface', namespaces) - interface_names = [interface.find('oc:name', namespaces).text for interface in interfaces] - interface_enabled=[interface.find('oc:config/oc:enabled', namespaces).text for interface in interfaces] - ip_address_element = root.find('.//ip:ip', ip_namespaces) - interface_prefix_length=root.find('.//ip:prefix-length',ip_namespaces) - if (len(interface_names) > 0): - dic['interface']={"name":interface_names[0],'ip':ip_address_element.text,'enabled':interface_enabled[0],"prefix-length":interface_prefix_length.text} - else : - dic['interface']={} - return dic + namespace = {'oc': 'http://openconfig.net/yang/interfaces'} + interfaces_result = [] + interfaces = root.findall('.//oc:interface',namespace) + #print(f"component {components}") + for interface in interfaces: + iface = {} + name = interface.find('.//oc:name',namespace) + enabled = interface.find('.//oc:enabled',namespace) + state= interface.find('.//oc:state',namespace) + + if state is not None : + ifindex=state.find('.//oc:ifindex',namespace) + operation_status = state.find('.//oc:oper-status',namespace) + + counters = state.find('.//oc:counters',namespace) + + if ifindex is not None : + iface["ifindex"]=ifindex.text + if operation_status is not None : + iface['operation_status']=operation_status.text + if counters is not None : + in_octets = counters.find('.//oc:in-octets',namespace) + in_pkts=counters.find('.//oc:in-pkts',namespace) + in_broadcast_pkts=counters.find('.//oc:in-broadcast-pkts',namespace) + in_multicast_pkts=counters.find('.//oc:in-multicast-pkts',namespace) + in_errors=counters.find('.//oc:in-errors',namespace) + in_discards=counters.find('.//oc:in-discards',namespace) + in_unicast_pkts=counters.find('.//oc:in-unicast-pkts',namespace) + + out_pkts=counters.find('.//oc:out-pkts',namespace) + out_broadcast_pkts=counters.find('.//oc:out-broadcast-pkts',namespace) + out_multicast_pkts=counters.find('.//oc:out-multicast-pkts',namespace) + out_errors=counters.find('.//oc:out-errors',namespace) + out_discards=counters.find('.//oc:out-discards',namespace) + out_unicast_pkts=counters.find('.//oc:out-unicast-pkts',namespace) + last_clear= counters.find('.//oc:last-clear',namespace) + if in_octets is not None : + iface["in_octets"]=in_octets.text + if in_pkts is not None : + iface["in_pkts"]=in_pkts.text + if in_broadcast_pkts is not None : + iface["in_broadcast_pkts"]=in_broadcast_pkts.text + if in_multicast_pkts is not None : + iface["in_multicast_pkts"]=in_multicast_pkts.text + if in_errors is not None : + iface["in_errors"]=in_errors.text + if in_discards is not None : + iface["in_discards"]=in_discards.text + if in_unicast_pkts is not None : + iface["in_unicast_pkts"]=in_unicast_pkts.text + if out_pkts is not None : + iface["out_pkts"]=out_pkts.text + if out_broadcast_pkts is not None : + iface["out_broadcast_pkts"]=out_broadcast_pkts.text + if out_multicast_pkts is not None : + iface["out_multicast_pkts"]=out_multicast_pkts.text + if out_errors is not None : + iface["out_errors"]=out_errors.text + if out_discards is not None : + iface["out_discards"]=out_discards.text + if out_unicast_pkts is not None : + iface["out_unicast_pkts"]=out_unicast_pkts.text + if last_clear is not None : + iface["last_clear"]=last_clear.text + + + if name is not None: + iface["name"]=name.text + if enabled is not None : + iface ["status"]=enabled.text + interfaces_result.append(iface) + + return interfaces_result + + def has_opticalbands(xml_data:str): xml_bytes = xml_data.encode("utf-8") @@ -246,6 +308,7 @@ def extract_ports_based_on_type (xml_data:str): root = ET.fromstring(xml_bytes) namespace = {'oc': 'http://openconfig.net/yang/platform', 'typex': 'http://openconfig.net/yang/platform-types'} ports = [] + ''' components = root.findall(".//oc:state[oc:type]",namespace) for component in components: type_ele = component.find(".//oc:type",namespace) @@ -256,6 +319,18 @@ def extract_ports_based_on_type (xml_data:str): port_index=name_element.text.split("-")[1] port = (port_name,port_index) ports.append(port) + ''' + optical_channel_namespaces = { + 'ns': 'urn:ietf:params:xml:ns:netconf:base:1.0', + 'oc': 'http://openconfig.net/yang/platform', + } + components = root.findall('.//oc:component',namespaces=optical_channel_namespaces) + for component in components: + name=component.find('.//oc:name',optical_channel_namespaces).text + if "port" in name: + port_index=name.split("-")[1] + port = (name,port_index) + ports.append(port) return ports def transponder_values_extractor(data_xml:str,resource_keys:list,dic:dict): @@ -268,8 +343,10 @@ def transponder_values_extractor(data_xml:str,resource_keys:list,dic:dict): channel_names= extract_channels_based_on_channelnamespace(xml_data=data_xml,channel_namespace=channel_namespace,is_opticalband=is_opticalband) ports = extract_ports_based_on_type(data_xml) + logging.info(f"OFC25---- {channel_names}, {ports}") optical_channels_params=[] ports_result=[] + interfaces=[] if (is_opticalband): endpoints=channel_names else: @@ -289,7 +366,7 @@ def transponder_values_extractor(data_xml:str,resource_keys:list,dic:dict): optical_channels_params.append(dic) #transceivers_dic=extract_tranceiver(data_xml=data_xml,dic={}) transceivers_dic={"transceiver":[]} - #interfaces_dic=extract_interface(xml_data=data_xml,dic={}) + interfaces=extract_interfaces(xml_data=data_xml) if len(ports)>0 : for port in ports : endpoint_name,endpoint_id=port @@ -298,4 +375,4 @@ def transponder_values_extractor(data_xml:str,resource_keys:list,dic:dict): ports_result.append((resource_key, resource_value)) - return [transceivers_dic,optical_channels_params,channel_namespace,endpoints,ports_result] + return [interfaces,optical_channels_params,channel_namespace,endpoints,ports_result] diff --git a/src/opticalcontroller/OpticalController.py b/src/opticalcontroller/OpticalController.py index 5026a6cd4752cc1f3d8c7e3fd5bd62f04cdd5cb5..9b0c330ece52f0264ef480aadf92875a79d27a73 100644 --- a/src/opticalcontroller/OpticalController.py +++ b/src/opticalcontroller/OpticalController.py @@ -17,15 +17,14 @@ from flask import Flask from flask import render_template from common.DeviceTypes import DeviceTypeEnum from flask_restplus import Resource, Api -from google.protobuf.json_format import MessageToDict -from common.proto.context_pb2 import TopologyId from opticalcontroller.tools import * from opticalcontroller.variables import * from opticalcontroller.RSA import RSA - -logging.basicConfig(level=logging.INFO) -LOGGER = logging.getLogger(__name__) - +from common.proto.context_pb2 import TopologyId , OpticalLink +import json +from google.protobuf.message import Message +from google.protobuf.json_format import MessageToDict +from common.tools.object_factory.OpticalLink import order_dict global rsa global links_dict rsa = None @@ -107,8 +106,6 @@ class AddFlexLightpath(Resource): return rsa.optical_bands[optical_band_id], 200 else: return "Error", 404 - - # @optical.route('/DelFlexLightpath////') @optical.route('/DelFlexLightpath////') @optical.route('/DelFlexLightpath/////') @@ -117,23 +114,23 @@ class AddFlexLightpath(Resource): class DelFLightpath(Resource): @staticmethod def delete( src, dst, bitrate, o_band_id, flow_id=None): - flow = None - match1 = False - ob_id = None + flow = None + match1=False + ob_id=None if flow_id is not None: + if flow_id in rsa.db_flows.keys(): - flow = rsa.db_flows[flow_id] - match1 = flow["src"] == src and flow["dst"] == dst and flow["bitrate"] == bitrate - ob_id = flow["parent_opt_band"] - flow['is_active'] = False - + flow = rsa.db_flows[flow_id] + match1 = flow["src"] == src and flow["dst"] == dst and flow["bitrate"] == bitrate + ob_id = flow["parent_opt_band"] + flow['is_active']=False if flow is not None: bidir = flow["bidir"] if bidir: match2 = flow["src"] == dst and flow["dst"] == src and flow["bitrate"] == bitrate if match1 or match2: ob_id = flow["parent_opt_band"] - rsa.del_flow(flow, ob_id) + rsa.del_flow(flow, flow_id, ob_id) rsa.db_flows[flow_id]["is_active"] = False if flow_id in rsa.optical_bands[ob_id]["served_lightpaths"]: rsa.optical_bands[ob_id]["served_lightpaths"].remove(flow_id) @@ -148,12 +145,15 @@ class DelFLightpath(Resource): return "flow {} not matching".format(flow_id), 404 else: if match1: + # if delete_band !=0 and ob_id is not None: # print(f"delete_lightpath {delete_band} and ob_id {ob_id}") # if len( rsa.optical_bands[ob_id]["served_lightpaths"]) != 0: # return "DELETE_NOT_ALLOWED" ,400 - rsa.del_flow(flow,flow_id,ob_id) - + + ob_id = flow["parent_opt_band"] + rsa.del_flow(flow,flow_id,ob_id) + if debug: print(f"vor ob_id {ob_id} rsa.optical_bands {rsa.optical_bands[ob_id]}") print(f"rsa.links_dict {rsa.links_dict}") @@ -162,31 +162,37 @@ class DelFLightpath(Resource): return "flow {} not matching".format(flow_id), 404 else: return "flow id {} does not exist".format(flow_id), 404 + + + -@optical.route('/DelOpticalBand///', methods=['DELETE']) +@optical.route('/DelOpticalBand///',methods=['DELETE']) @optical.response(200, 'Success') @optical.response(404, 'Error, not found') class DelOpticalBand(Resource): @staticmethod def delete( src, dst, o_band_id): flow = None - ob_id = None - if o_band_id is not None: - if o_band_id in rsa.optical_bands.keys(): - flow = rsa.optical_bands[o_band_id] - match1 = flow["src"] == src and flow["dst"] == dst and flow["bitrate"] - ob_id = o_band_id + ob_id=None + if o_band_id is not None: + + if o_band_id in rsa.optical_bands.keys(): + flow=rsa.optical_bands[o_band_id] + #match1 = flow["src"] == src and flow["dst"] == dst + ob_id=o_band_id + if flow is not None: bidir = flow["bidir"] if bidir: - match2 = flow["src"] == dst and flow["dst"] == src and flow["bitrate"] + match1 = flow["src"] == src and flow["dst"] == dst and flow["bitrate"] + match2 = flow["src"] == dst and flow["dst"] == src and flow["bitrate"] if match1 or match2: ob_id = flow["parent_opt_band"] #rsa.del_flow(flow, ob_id) rsa.optical_bands[ob_id]["is_active"] = False - # if flow_id in rsa.optical_bands[ob_id]["served_lightpaths"]: + # if flow_id in rsa.optical_bands[ob_id]["served_lightpaths"].remove: # rsa.optical_bands[ob_id]["served_lightpaths"].remove(flow_id) #if rsa.optical_bands[ob_id]["reverse_optical_band_id"] != 0: # rev_ob_id = rsa.optical_bands[ob_id]["reverse_optical_band_id"] @@ -213,6 +219,48 @@ class DelOpticalBand(Resource): return "flow for ob_id {} not found".format(ob_id),400 else: return "ob_id {} does not exist".format(ob_id), 404 + + + +@optical.route('/DelOpticalBandSimple/',methods=['DELETE']) +@optical.response(200, 'Success') +@optical.response(404, 'Error, not found') +class DelOpticalBandSimple(Resource): + @staticmethod + def delete(o_band_id): + flow = None + ob_id=None + if o_band_id is not None: + if o_band_id in rsa.optical_bands.keys(): + flow=rsa.optical_bands[o_band_id] + ob_id=o_band_id + if flow is not None: + bidir = flow["bidir"] + if bidir: + if len( rsa.optical_bands[ob_id]["served_lightpaths"]) != 0: + return "DELETE_NOT_ALLOWED" ,400 + rsa.optical_bands[ob_id]["is_active"] = False + rsa.del_band(flow,ob_id) + if debug: + print(rsa.links_dict) + return "ob_id {} deleted".format(ob_id), 200 + else: + if len( rsa.optical_bands[ob_id]["served_lightpaths"]) != 0: + return "DELETE_NOT_ALLOWED" ,400 + rsa.optical_bands[ob_id]["is_active"] = False + rsa.del_band(flow,ob_id) + if debug: + print(f"vor ob_id {ob_id} rsa.optical_bands {rsa.optical_bands[ob_id]}") + print(f"rsa.links_dict {rsa.links_dict}") + return "ob_id {} deleted".format(ob_id), 200 + + else : + return "flow for ob_id {} not found".format(ob_id),400 + else: + return "ob_id {} does not exist".format(ob_id), 404 + + + @optical.route('/DelLightpath////') @@ -250,7 +298,6 @@ class GetFlows(Resource): except: return "Error", 404 - @optical.route('/GetOpticalBands') @optical.response(200, 'Success') @optical.response(404, 'Error, not found') @@ -296,8 +343,8 @@ class GetFlows(Resource): return links, 200 except: return "Error", 404 - - + + @optical.route('/GetTopology//',methods=['GET']) @optical.response(200, 'Success') @optical.response(404, 'Error, not found') @@ -313,10 +360,10 @@ class GetTopology(Resource): topog_id.topology_uuid.uuid=topology_id topog_id.context_id.context_uuid.uuid=context_id - try: - links_dict = {"optical_links": []} + try: + links_dict={"optical_links":[]} node_dict = {} - topo, nodes = readTopologyDataFromContext(topog_id) + topo , nodes = readTopologyDataFromContext(topog_id) OPTICAL_ROADM_TYPES = { DeviceTypeEnum.OPTICAL_ROADM.value, DeviceTypeEnum.EMULATED_OPTICAL_ROADM.value @@ -345,7 +392,7 @@ class GetTopology(Resource): #i+=1 #print(f"refresh_optical controller optical_links_dict= {links_dict}") #print(f"refresh_optical controller node_dict {node_dict}") - + for link in topo: endpoint_id_a = link.link_endpoint_ids[ 0] endpoint_id_z = link.link_endpoint_ids[-1] @@ -357,26 +404,28 @@ class GetTopology(Resource): if device_uuid_z not in added_device_uuids: continue link_dict_type = MessageToDict(link, preserving_proto_field_name=True) - - if "c_slots" in link_dict_type["optical_details"]: - link_dict_type["optical_details"]["c_slots"] = link_dict_type["optical_details"]["c_slots"] - - if "l_slots" in link_dict_type["optical_details"]: - link_dict_type["optical_details"]["l_slots"] = link_dict_type["optical_details"]["l_slots"] - - if "s_slots" in link_dict_type["optical_details"]: - link_dict_type["optical_details"]["s_slots"] = link_dict_type["optical_details"]["s_slots"] - + + if "c_slots" in link_dict_type["optical_details"] : + link_dict_type["optical_details"]["c_slots"]=order_dict(link_dict_type["optical_details"]["c_slots"]) + + if "l_slots" in link_dict_type["optical_details"] : + link_dict_type["optical_details"]["l_slots"]=order_dict(link_dict_type["optical_details"]["l_slots"]) + + if "s_slots" in link_dict_type["optical_details"] : + link_dict_type["optical_details"]["s_slots"]=order_dict(link_dict_type["optical_details"]["s_slots"]) + links_dict["optical_links"].append(link_dict_type) - + rsa = RSA(node_dict, links_dict) if debug: + print(f'rsa.init_link_slots2() {rsa}') print(rsa.init_link_slots2()) - return "ok", 200 + + + return "ok" ,200 except Exception as e: - LOGGER.exception('Error in GetTopology') print(f"err {e}") return "Error", 400 - -if __name__ == '__main__': + +if __name__ == '__main__': app.run(host='0.0.0.0', port=10060, debug=True) diff --git a/src/opticalcontroller/RSA.py b/src/opticalcontroller/RSA.py index 5a6f1c03ca714ddc1919bc58a45753c9c6964fc9..82bd9c2326b5ad78d6487f025a47b3599b67c239 100644 --- a/src/opticalcontroller/RSA.py +++ b/src/opticalcontroller/RSA.py @@ -13,7 +13,7 @@ # limitations under the License. import logging -from opticalcontroller.dijkstra import Graph, shortest_path +from opticalcontroller.dijkstra import * from opticalcontroller.tools import * from opticalcontroller.variables import * @@ -36,8 +36,9 @@ class RSA(): self.l_slot_number = 0 self.s_slot_number = 0 self.optical_bands = {} - - def init_link_slots(self): + + + def init_link_slots(self, testing): if full_links: for l in self.links_dict["optical_links"]: for fib in l["optical_link"]["details"]["fibers"]: @@ -271,8 +272,18 @@ class RSA(): fib[band][str(i)] = 0 if 'used' in fib: fib['used'] = True - print(f"fib updated {fib}") + print(f"fib updated {fib}") #print(fib) + + def update_link_2(self, fib, slots, band,link): + #print(fib) + for i in slots: + fib[band][str(i)] = 0 + if 'used' in fib: + fib['used'] = True + + set_link_update(fib,link) + #print(fib) def update_optical_band(self, optical_band_id, slots, band): for i in slots: @@ -288,25 +299,43 @@ class RSA(): if 'used' in fib: fib['used'] = False #fib[band].sort() + + def restore_link_2(self, fib, slots, band, link): + print("start restoring link") + for i in slots: + fib[band][str(i)] = 1 + if 'used' in fib: + fib['used'] = False + #fib[band].keys().sort() + #set_link_update(fib,link,test="restoration") + def restore_optical_band(self, optical_band_id, slots, band): + for i in slots: self.optical_bands[optical_band_id][band][str(i)] = 1 + #self.optical_bands[optical_band_id][band].append(int(i)) #self.optical_bands[optical_band_id][band].sort() + + + + + def restore_optical_band_2(self, optical_band_id, slots, band ,links): print(f"example of band { band}") print(f"example of slots {slots}") print(f"example of self.optical_bands_before { self.optical_bands}") for i in slots: self.optical_bands[optical_band_id][band][str(i)] = 1 - print(f"example of self.optical_bands_after { self.optical_bands}") - + print(f"example of self.optical_bands_after { self.optical_bands}") + #link_name= self.optical_bands[optical_band_id]['links'][0] #link = self.get_link_by_name(link_name) #update_optical_band(optical_bands=self.optical_bands,optical_band_id=optical_band_id,band=band,link=link) - + + def del_flow(self, flow,flow_id, o_b_id = None): flows = flow["flows"] band = flow["band_type"] @@ -320,19 +349,24 @@ class RSA(): bidir = flow["bidir"] flow_id = flow["flow_id"] + for l in links: if debug: print(l) #link = self.links_dict[l] #f = fiber_f[l] #fib = link['fibers'][f] - fib = self.get_link_by_name(l)["optical_details"] - - self.restore_link(fib, slots, band) + link = self.get_link_by_name(l) + fib = link["optical_details"] + #self.restore_link(fib, slots, band) + self.restore_link_2(fib, slots, band, link=link) if debug: print(fib[band]) - + + + if o_b_id is not None: + if debug: print("restoring OB") print(f"invoking restore_optical_band o_b_id: {o_b_id} , slots {slots} , band {band} ") @@ -342,18 +376,17 @@ class RSA(): self.optical_bands[o_b_id]["served_lightpaths"].remove(flow_id) #self.restore_optical_band_2(o_b_id, slots, band,links) - if bidir: for l in links: r_l = reverse_link(l) if debug: print(r_l) - # link = self.links_dict[l] - # f = fiber_f[l] - # fib = link['fibers'][f] - fib = self.get_link_by_name(r_l)["optical_details"] + rlink = self.get_link_by_name(r_l) + fib = rlink["optical_details"] + #fib = self.get_link_by_name(r_l)["optical_details"] if list_in_list(slots, str_list_to_int(fib[band].keys())): - self.restore_link(fib, slots, band) + #self.restore_link(fib, slots, band, link=l) + self.restore_link_2(fib, slots, band, link=rlink) if debug: print(fib[band]) ''' @@ -378,7 +411,9 @@ class RSA(): def del_band(self, flow, o_b_id = None): + print(f"delete band {flow} ") + flows = flow["flows"] band = None @@ -390,70 +425,59 @@ class RSA(): path = flow["path"] bidir = flow["bidir"] links = [] + slots = [] if o_b_id is not None: - links= self.optical_bands[o_b_id]["links"] - band = self.optical_bands[o_b_id]["band_type"] - n_slots =self.optical_bands[o_b_id]["n_slots"] - if n_slots > 0: - slots=[i+1 for i in range(n_slots)] + links= self.optical_bands[o_b_id]["links"] + band = self.optical_bands[o_b_id]["band_type"] + n_slots =self.optical_bands[o_b_id]["n_slots"] + if n_slots > 0: + #slots=[i+1 for i in range(n_slots)] + for key in self.optical_bands[o_b_id][band].keys(): + slots.append(int(key)) + slots.sort() + - for l in links: - if debug: - print(l) - #link = self.links_dict[l] - #f = fiber_f[l] - #fib = link['fibers'][f] - fib = self.get_link_by_name(l)["optical_details"] - print(f"del_flow_fib {fib } and band {band}") - print(f"del_flow { str_list_to_int(fib[band].keys())}") - - print(f"invoking restore_link fib: {fib} , slots {slots} , band {band} ") - self.restore_link(fib, slots, band) - self.optical_bands[o_b_id]["is_active"]=False - - if debug: - print(fib[band]) + for l in links: + if debug: + print(l) + #fib = self.get_link_by_name(l)["optical_details"] + link = self.get_link_by_name(l) + fib = link["optical_details"] + print(f"del_flow_fib {fib } and band {band}") + print(f"del_flow { str_list_to_int(fib[band].keys())}") - if o_b_id is not None: + print(f"invoking restore_link_2 fib: {fib} , slots {slots} , band {band} ") + #self.restore_link(fib, slots, band) + self.restore_link_2(fib, slots, band, link = link) + + self.optical_bands[o_b_id]["is_active"]=False + if debug: + print(fib[band]) if debug: print("restoring OB") print(f"invoking restore_optical_band o_b_id: {o_b_id} , slots {slots} , band {band} ") self.restore_optical_band(o_b_id, slots, band) #self.restore_optical_band_2(o_b_id, slots, band,links) - if bidir: - for l in links: - r_l = reverse_link(l) - if debug: - print(r_l) - # link = self.links_dict[l] - # f = fiber_f[l] - # fib = link['fibers'][f] - fib = self.get_link_by_name(r_l)["optical_details"] - if list_in_list(slots, str_list_to_int(fib[band].keys())): - self.restore_link(fib, slots, band) - if debug: - print(fib[band]) - ''' - for rl in fiber_b.keys(): - if debug: - print(rl) - print(fiber_b[rl]) - #rlink = self.links_dict[rl] - #rf = fiber_b[rl] - #rfib = rlink['fibers'][rf] - rfib = self.get_fiber_details(rl, fiber_b[rl]) - if not list_in_list(slots, rfib[band]): - self.restore_link(rfib, slots, band) + if bidir: + for l in links: + r_l = reverse_link(l) if debug: - print(rfib[band]) - ''' - #changed according to TFS development - #if o_b_id is not None: - # rev_o_band_id = self.optical_bands[o_b_id]["reverse_optical_band_id"] - # self.restore_optical_band(rev_o_band_id, slots, band) + print(r_l) + rlink = self.get_link_by_name(r_l) + fib = rlink["optical_details"] + #fib = self.get_link_by_name(r_l)["optical_details"] + if list_in_list(slots, str_list_to_int(fib[band].keys())): + self.restore_link_2(fib, slots, band, link=rlink) + if debug: + print(fib[band]) + #changed according to TFS development + #if o_b_id is not None: + # rev_o_band_id = self.optical_bands[o_b_id]["reverse_optical_band_id"] + # self.restore_optical_band(rev_o_band_id, slots, band) return True - + + def del_handler(self, flow,flow_id, o_b_id = None,delete_band=0): print(f" del_handler flow {flow} flow_id {flow_id} o_b_id {o_b_id} delete_band {delete_band}") if delete_band != 0: @@ -461,7 +485,9 @@ class RSA(): self.del_band(flow,flow_id,o_b_id=o_b_id) else : self.del_flow(flow,flow_id=flow_id,o_b_id=o_b_id) - + + + def get_fibers_forward(self, links, slots, band): fiber_list = {} add = links[0] @@ -496,7 +522,8 @@ class RSA(): continue if list_in_list(slots, str_list_to_int(fib[band].keys())): #fiber_list[l] = fib["ID"] - self.update_link(fib, slots, band) + #self.update_link(fib, slots, band) + self.update_link_2(fib,slots,band,link) break print("INFO: Path forward computation completed") return fiber_list @@ -820,7 +847,13 @@ class RSA(): def create_optical_band(self, links, path, bidir, num_slots): print("INFO: Creating optical-band of {} slots".format(num_slots)) - self.opt_band_id += 1 + if self.opt_band_id == 0: + self.opt_band_id += 1 + else: + if (self.optical_bands[self.opt_band_id]["bidir"] == 1): + self.opt_band_id += 2 + else: + self.opt_band_id += 1 forw_opt_band_id = self.opt_band_id self.optical_bands[forw_opt_band_id] = {} self.optical_bands[forw_opt_band_id]["optical_band_id"] = forw_opt_band_id @@ -978,7 +1011,13 @@ class RSA(): optical_band_id, temp_links = self.create_optical_band(links, path, bidir, num_slots_ob) return None, optical_band_id print("INFO: TP to TP connection") - self.flow_id += 1 + if self.flow_id == 0: + self.flow_id += 1 + else: + if (self.db_flows[self.flow_id]["bidir"] == 1): + self.flow_id += 2 + else: + self.flow_id += 1 self.db_flows[self.flow_id] = {} self.db_flows[self.flow_id]["flow_id"] = self.flow_id self.db_flows[self.flow_id]["src"] = src @@ -1118,8 +1157,8 @@ class RSA(): self.db_flows[self.flow_id]["freq"] = f0 self.db_flows[self.flow_id]["is_active"] = True self.db_flows[self.flow_id]["parent_opt_band"] = ob_id - self.db_flows[self.flow_id]["new_optical_band"] = 1 - #self.db_flows[self.flow_id]["new_optical_band"] = 2 + #self.db_flows[self.flow_id]["new_optical_band"] = 1 + self.db_flows[self.flow_id]["new_optical_band"] = 2 self.optical_bands[ob_id]["served_lightpaths"].append(self.flow_id) ''' if bidir: @@ -1180,6 +1219,7 @@ class RSA(): rev_ob_id = self.optical_bands[optical_band_id]["reverse_optical_band_id"] self.optical_bands[rev_ob_id]["served_lightpaths"].append(self.flow_id) ''' + return self.flow_id, optical_band_id def extend_optical_band(self, ob_id, band=None): @@ -1218,4 +1258,5 @@ class RSA(): link = self.get_link_by_name(link_x) fib = link["optical_details"] self.update_link(fib, new_slots, band_type) + return new_slots diff --git a/src/opticalcontroller/tools.py b/src/opticalcontroller/tools.py index 290c8f70b0aa46c8370dc6430ed19225ee4a0475..b9a3e79b6f95fc7e2723716f23bb7e710866ec2e 100644 --- a/src/opticalcontroller/tools.py +++ b/src/opticalcontroller/tools.py @@ -12,25 +12,30 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json import numpy as np +from opticalcontroller.variables import * +import json , logging +from context.client.ContextClient import ContextClient from common.proto.context_pb2 import TopologyId , LinkId , OpticalLink , OpticalLinkDetails from common.tools.object_factory.OpticalLink import correct_slot -from context.client.ContextClient import ContextClient -from opticalcontroller.variables import * def common_slots(a, b): return list(np.intersect1d(a, b)) -def map_modulation_to_op(mod): +def map_modulation_to_op(mod, rate): if mod == "DP-QPSK": return 1 elif mod == "DP-8QAM": - return 4 + return 2 elif mod == "DP-16QAM": - return 8 + if rate == 400: + return 4 + elif rate == 800: + return 8 + else: + return 18 elif mod == "DP-32QAM": return 10 @@ -43,23 +48,22 @@ def map_rate_to_slot(rate): if rate == 100: mod = "DP-QPSK" slots = 4 - op = map_modulation_to_op(mod) + op = map_modulation_to_op(mod, rate) return op, slots elif rate == 400: mod = "DP-8QAM" - slots = 4 - op = map_modulation_to_op(mod) + slots = 8 #100GHz + op = map_modulation_to_op(mod, rate) return op, slots elif rate == 800: mod = "DP-16QAM" - #todo: check slot width - slots = 8#12 (150GHz) - op = map_modulation_to_op(mod) + slots = 12 #150GHz + op = map_modulation_to_op(mod, rate) return op, slots elif rate == 1000: mod = "DP-32QAM" slots = 18 - op = map_modulation_to_op(mod) + op = map_modulation_to_op(mod, rate) return op, slots else: return 2, 5 @@ -190,14 +194,16 @@ def frequency_converter(b, slots): def readTopologyData(nodes, topology): - nodes_file = open(nodes, 'r') - topo_file = open(topology, 'r') - nodes = json.load(nodes_file) - topo = json.load(topo_file) - #print(topo) - nodes_file.close() - topo_file.close() - return nodes, topo + + + nodes_file = open(nodes, 'r') + topo_file = open(topology, 'r') + nodes = json.load(nodes_file) + topo = json.load(topo_file) + #print(topo) + nodes_file.close() + topo_file.close() + return nodes, topo def readTopologyDataFromContext(topology_id:TopologyId): @@ -261,5 +267,66 @@ def handle_slot (slot_field, slot): slot_field[key]=value + +def update_optical_band (optical_bands,optical_band_id,band,link): + key_list = optical_bands[optical_band_id][band].keys() + corrected_slots=optical_bands[optical_band_id][band] + if (len(key_list) < 20): + corrected_slots=correct_slot(optical_bands[optical_band_id][band]) + + fib={} + fib['c_slots']=link['optical_details']['c_slots'] + fib['l_slots']=link['optical_details']['l_slots'] + fib['s_slots']=link['optical_details']['s_slots'] + + fib[band]=corrected_slots + fib["src_port"]=optical_bands[optical_band_id]['src_port'] + fib["dst_port"]=optical_bands[optical_band_id]['dst_port'] + fib["local_peer_port"]=link["optical_details"]["local_peer_port"] + fib["remote_peer_port"]=link["optical_details"]["remote_peer_port"] + set_link_update(fib,link,test=f"restoring_optical_band {link['link_id']}") + +def set_link_update (fib:dict,link:dict,test="updating"): + #print(link) + print(f"invoked from {test}") + print(f"fib updated {fib}") + optical_link = OpticalLink() + linkId = LinkId() + #linkId.link_uuid.uuid=link["link_id"]["link_uuid"]["uuid"] + linkId.link_uuid.uuid=link["link_id"]["link_uuid"]["uuid"] + optical_details = OpticalLinkDetails() + optical_link.optical_details.length=0 + if "src_port" in fib : + optical_link.optical_details.src_port=fib["src_port"] + if "dst_port" in fib : + optical_link.optical_details.dst_port=fib["dst_port"] + if "local_peer_port" in fib : + optical_link.optical_details.local_peer_port=fib['local_peer_port'] + if "remote_peer_port" in fib: + optical_link.optical_details.remote_peer_port=fib['remote_peer_port'] + + optical_link.optical_details.used=fib['used'] if 'used' in fib else False + if "c_slots" in fib : + + handle_slot( optical_link.optical_details.c_slots,fib["c_slots"]) + if "s_slots" in fib : + + handle_slot( optical_link.optical_details.s_slots,fib["s_slots"]) + if "l_slots" in fib : + + handle_slot( optical_link.optical_details.l_slots,fib["l_slots"]) + + + optical_link.name=link['name'] + + optical_link.link_id.CopyFrom(linkId) + + ctx_client = ContextClient() + ctx_client.connect() + try: + ctx_client.SetOpticalLink(optical_link) + except Exception as err: + print (f"setOpticalLink {err}") + diff --git a/src/service/service/ServiceServiceServicerImpl.py b/src/service/service/ServiceServiceServicerImpl.py index bf923eed9880353b5fbde291dda1e66ba3f600e9..fb4537925d5a287e32e2f0ad63a8fcf5d3244989 100644 --- a/src/service/service/ServiceServiceServicerImpl.py +++ b/src/service/service/ServiceServiceServicerImpl.py @@ -44,7 +44,8 @@ from .task_scheduler.TaskScheduler import TasksScheduler from .tools.GeodesicDistance import gps_distance from .tools.OpticalTools import ( add_lightpath, delete_lightpath, adapt_reply, get_device_name_from_uuid, - get_optical_band, refresh_opticalcontroller, DelFlexLightpath + get_optical_band, refresh_opticalcontroller, DelFlexLightpath , extend_optical_band + ) @@ -276,7 +277,10 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): ports = [] for endpoint_id in service.service_endpoint_ids: endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid - endpoint_device_name = device_names[endpoint_device_uuid] + if "." in endpoint_device_uuid: + endpoint_device_name = endpoint_device_uuid + else: + endpoint_device_name = device_names[endpoint_device_uuid] devs.append(endpoint_device_name) ports.append(endpoint_id.endpoint_uuid.uuid) src = devs[0] @@ -299,6 +303,7 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): reply_json = json.loads(reply_txt) LOGGER.debug('[optical] reply_json[{:s}]={:s}'.format(str(type(reply_json)), str(reply_json))) optical_band_txt = "" + if "new_optical_band" in reply_json.keys(): if reply_json["new_optical_band"] == 1: if reply_json["parent_opt_band"]: @@ -311,6 +316,16 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): LOGGER.debug('expected optical band not found') else: LOGGER.debug('expected optical band not found') + elif reply_json["new_optical_band"]==2 : + parent_ob = reply_json["parent_opt_band"] + LOGGER.debug('Parent optical-band={}'.format(parent_ob)) + optical_band_txt = get_optical_band(parent_ob) + + + service_expansion= extend_optical_band(reply_json,optical_band_txt) + tasks_scheduler.compose_from_service_expansion(service_expansion) + + else: LOGGER.debug('Using existing optical band') else: @@ -321,7 +336,7 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): devices, _service, reply_json, context_uuid_x, topology_uuid_x, optical_band_txt ) - tasks_scheduler.compose_from_pathcompreply( + tasks_scheduler.compose_from_opticalcontroller_reply( optical_reply, is_delete=False) else: if len(service_with_uuids.service_endpoint_ids) >= num_expected_endpoints: @@ -374,6 +389,7 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): "dst" : None, "bitrate" : None, 'ob_id' : None, + 'bidir' : None, 'flow_id' : None } devs = [] @@ -389,6 +405,7 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): src = get_device_name_from_uuid(devices, devs[0]) dst = get_device_name_from_uuid(devices, devs[1]) bitrate = 100 + bidir = 0 for constraint in service.service_constraints: if "bandwidth" in constraint.custom.constraint_type: bitrate = int(float(constraint.custom.constraint_value)) @@ -400,22 +417,22 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): if len(service.service_config.config_rules) > 0: c_rules_dict = json.loads( service.service_config.config_rules[0].custom.resource_value) - ob_id = None - flow_id = None - + ob_id=None + flow_id=None if "ob_id" in c_rules_dict: ob_id = c_rules_dict["ob_id"] if ("flow_id" in c_rules_dict): flow_id = c_rules_dict["flow_id"] #if ("ob_id" in c_rules_dict): # ob_id = c_rules_dict["ob_id"] - params['bitrate'] = bitrate params['dst' ] = dst params['src' ] = src params['ob_id' ] = ob_id params['flow_id'] = flow_id - + params['bidir' ] = bidir + + tasks_scheduler = TasksScheduler(self.service_handler_factory) tasks_scheduler.compose_from_optical_service(service, params=params, is_delete=True) tasks_scheduler.execute_all() diff --git a/src/service/service/service_handler_api/FilterFields.py b/src/service/service/service_handler_api/FilterFields.py index 2406ca5f042c516876f328e614db44c097a6f7ec..14f975316ce3c83d95467b1a99549d5862dd3d59 100644 --- a/src/service/service/service_handler_api/FilterFields.py +++ b/src/service/service/service_handler_api/FilterFields.py @@ -29,9 +29,9 @@ SERVICE_TYPE_VALUES = { ServiceTypeEnum.SERVICETYPE_E2E, ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY, ServiceTypeEnum.SERVICETYPE_QKD, - ServiceTypeEnum.SERVICETYPE_IP_LINK ServiceTypeEnum.SERVICETYPE_INT, ServiceTypeEnum.SERVICETYPE_ACL, + ServiceTypeEnum.SERVICETYPE_IP_LINK, } DEVICE_DRIVER_VALUES = { diff --git a/src/service/service/service_handler_api/_ServiceHandler.py b/src/service/service/service_handler_api/_ServiceHandler.py index 3c295dfb155dd8387a227e135a59434cf35d1a7b..db779681c5ea52bf261885c392daeebccd825a88 100644 --- a/src/service/service/service_handler_api/_ServiceHandler.py +++ b/src/service/service/service_handler_api/_ServiceHandler.py @@ -150,3 +150,25 @@ class _ServiceHandler: the processing must be returned. """ raise NotImplementedError() + + + def SetOpticalConfig ( + self, endpoints : List[Tuple[str, str, Optional[str]]], + connection_uuid : Optional[str] = None + ) -> List[Union[bool, Exception]]: + + """ Create/Update Optical configuration related to optical service. + Parameters: + endpoints: List[Tuple[str, str, Optional[str]]] + List of tuples, each containing a device_uuid, + endpoint_uuid and, optionally, the topology_uuid + of the endpoint to be added. + connection_uuid : Optional[str] + If specified, is the UUID of the connection this endpoint is associated to. + Returns: + results: List[Union[bool, Exception]] + List of results for configurations changes requested. + Return values must be in the same order as the requested + configurtations. + """ + diff --git a/src/service/service/service_handlers/oc/OCServiceHandler.py b/src/service/service/service_handlers/oc/OCServiceHandler.py index 3235b4046d344a499d7cfc0cf577ab519b63831a..01ebaebbe1e5ec31e9e5c8d4b6888dead103ff24 100644 --- a/src/service/service/service_handlers/oc/OCServiceHandler.py +++ b/src/service/service/service_handlers/oc/OCServiceHandler.py @@ -25,7 +25,7 @@ from service.service.service_handler_api.SettingsHandler import SettingsHandler from service.service.task_scheduler.TaskExecutor import TaskExecutor from .ConfigRules import setup_config_rules, teardown_config_rules from .OCTools import ( - convert_endpoints_to_flows + endpoints_to_flows,convert_endpoints_to_flows #handle_flows_names, check_media_channel_existance ) @@ -48,26 +48,7 @@ class OCServiceHandler(_ServiceHandler): chk_type('endpoints', endpoints, list) if len(endpoints) == 0: return [] - is_opticalband =False - #service_uuid = self.__service.service_id.service_uuid.uuid - settings=None - - if self.__settings_handler.get('/settings-ob_{}'.format(connection_uuid)): - is_opticalband=True - settings = self.__settings_handler.get('/settings-ob_{}'.format(connection_uuid)) - else: - settings = self.__settings_handler.get('/settings') - - bidir = settings.value.get("bidir") - LOGGER.debug(f"Bidir bvalue is: {bidir}") - # settings = self.__settings_handler.get('/settings') - - #flow is the new variable that stores input-output relationship - flows = convert_endpoints_to_flows(endpoints) - LOGGER.info(f"endpoints {endpoints} is_opticalband {is_opticalband} ") - #flows = endpoints_to_flows(endpoints, bidir, is_opticalband) - #handled_flows=handle_flows_names(flows=flows,task_executor=self.__task_executor) - + results = [] for endpoint in endpoints: try: @@ -91,16 +72,57 @@ class OCServiceHandler(_ServiceHandler): LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint))) results.append(e) - LOGGER.info(f"flows {flows} ") - LOGGER.info(f"settings {settings} ") + + return results + + + @metered_subclass_method(METRICS_POOL) + def SetOpticalConfig( + self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None + ) -> List[Union[bool, Exception]]: + + LOGGER.info(f"endpoints {endpoints} ") + + chk_type('endpoints', endpoints, list) + if len(endpoints) == 0: return [] + is_opticalband =False + #service_uuid = self.__service.service_id.service_uuid.uuid + settings=None + results = [] + if self.__settings_handler.get('/settings-ob_{}'.format(connection_uuid)): + is_opticalband=True + settings = self.__settings_handler.get('/settings-ob_{}'.format(connection_uuid)) + else: + settings = self.__settings_handler.get('/settings') + + bidir = settings.value.get("bidir") + LOGGER.debug(f"settings bvalue is: {settings}") + # settings = self.__settings_handler.get('/settings') + + # in case service expanded , the only part to reconfigure is optical band + ob_expansion =settings.value.get('ob-expanded',None) + if ob_expansion : + if not is_opticalband: + LOGGER.debug(f"ob-expanded bvalue is: {ob_expansion} and is_opticalband {is_opticalband}") + return results + LOGGER.info(f"endpoints {endpoints} is_opticalband {is_opticalband} ") + #flow is the new variable that stores input-output relationship + #flows = convert_endpoints_to_flows(endpoints) + + flows = endpoints_to_flows(endpoints, bidir, is_opticalband) + LOGGER.info(f"endpoints {flows} is_opticalband {is_opticalband} ") + #handled_flows=handle_flows_names(flows=flows,task_executor=self.__task_executor) + + #new cycle for setting optical devices for device_uuid, dev_flows in flows.items(): try: device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) LOGGER.info(f"device {device_obj.name} ") - if settings: - self.__task_executor.configure_optical_device(device_obj, settings, dev_flows, is_opticalband) + if settings is not None: + self.__task_executor.configure_optical_device(device_obj, settings, dev_flows + , is_opticalband ,connection_uuid) results.append(True) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unable to configure Device({:s})'.format(str(device_uuid))) @@ -108,22 +130,25 @@ class OCServiceHandler(_ServiceHandler): return results + @metered_subclass_method(METRICS_POOL) def DeleteEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: is_opticalband =False - flows = convert_endpoints_to_flows(endpoints) + service_uuid = self.__service.service_id.service_uuid.uuid chk_type('endpoints', endpoints, list) if len(endpoints) == 0: return [] - + if self.__settings_handler.get('/settings-ob_{}'.format(connection_uuid)): is_opticalband =True settings = self.__settings_handler.get('/settings-ob_{}'.format(connection_uuid)) else: settings = self.__settings_handler.get('/settings') - + + bidir = settings.value.get("bidir",None) + flows = convert_endpoints_to_flows(endpoints) results = [] for endpoint in endpoints: @@ -150,7 +175,6 @@ class OCServiceHandler(_ServiceHandler): except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint))) results.append(e) - for device_uuid, dev_flows in flows.items(): try: channel_indexes= [] @@ -179,7 +203,8 @@ class OCServiceHandler(_ServiceHandler): if len(channel_indexes) > 0: errors = self.__task_executor.deconfigure_optical_device( device=device_obj, channel_indexes=channel_indexes, - is_opticalband=is_opticalband, dev_flow=dev_flows + is_opticalband=is_opticalband, dev_flow=dev_flows, + bidir=bidir ) # if (len(errors)==0): # service_id =self.__service.service_id diff --git a/src/service/service/service_handlers/oc/OCTools.py b/src/service/service/service_handlers/oc/OCTools.py index ccddbdbd17486c5f950a8adf847c56c7ca838b28..24bc7b384dab6edd3c44b62a7448914857b86ee9 100644 --- a/src/service/service/service_handlers/oc/OCTools.py +++ b/src/service/service/service_handlers/oc/OCTools.py @@ -20,7 +20,100 @@ from service.service.service_handler_api.Tools import get_endpoint_matching log = logging.getLogger(__name__) -#def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]])->Dict[str: List[Tuple[str, str]]]: +def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]])->Dict: + #entries = List[Tuple[str, str, str, Optional[str]]] + #entries = Dict[str: List[Tuple[str, str]]] + entries = {} + #tuple is in, out + #end = len(endpoints) if isinstance(endpoints,list) else 0 + end = len(endpoints) + i = 0 + bidir = 0 + log.debug("end={}".format(end)) + while(i < end): + endpoint = endpoints[i] + device_uuid, endpoint_uuid = endpoint[0:2] + log.info("current OCTools step {}, {}, {}".format(i, device_uuid, endpoint_uuid)) + if device_uuid not in entries.keys(): + entries[device_uuid] = [] + if i == 0: + entry_tuple = "0", endpoint_uuid + entries[device_uuid].append(entry_tuple) + next_endpoint = endpoints[i+1] + next_device_uuid, next_endpoint_uuid = next_endpoint[0:2] + if next_device_uuid == device_uuid: + bidir = 1 + log.info("connection is bidirectional") + entry_tuple = next_endpoint_uuid, "0" + entries[device_uuid].append(entry_tuple) + i = i + 1 + else: + log.debug("connection is unidirectional") + else: + if not bidir: + if i == end-1: + #is the last node + entry_tuple = endpoint_uuid, "0" + entries[device_uuid].append(entry_tuple) + else: + #it is a transit node + next_endpoint = endpoints[i+1] + next_device_uuid, next_endpoint_uuid = next_endpoint[0:2] + if next_device_uuid == device_uuid: + entry_tuple = endpoint_uuid, next_endpoint_uuid + entries[device_uuid].append(entry_tuple) + i = i + 1 + log.info("current OCTools step {}, {}, {}".format(i, next_device_uuid, device_uuid)) + else: + log.debug("ERROR in unidirectional connection 4") + return {} + else: + log.debug("Ocheck i {}, {}, {}".format(i, i+1, end-1)) + if i + 1 == end-1: + log.debug("current OCTools step {}, {}, {}".format(i, device_uuid, endpoint_uuid)) + #is the last node + entry_tuple = endpoint_uuid, "0" + entries[device_uuid].append(entry_tuple) + next_endpoint = endpoints[i+1] + log.debug("OCTools i+1 step {}, {}, {}".format(i+1, next_device_uuid, device_uuid)) + + next_device_uuid, next_endpoint_uuid = next_endpoint[0:2] + if next_device_uuid == device_uuid: + entry_tuple = "0", next_endpoint_uuid + entries[device_uuid].append(entry_tuple) + i = i + 1 + else: + log.debug("ERROR in bidirectional connection 2") + return entries + else: + log.debug("OCTools i+1+2+3 step {}, {}, {}".format(i+1, next_device_uuid, device_uuid)) + #i+1 + next_endpoint = endpoints[i+1] + next_device_uuid, next_endpoint_uuid = next_endpoint[0:2] + if next_device_uuid == device_uuid: + entry_tuple = endpoint_uuid, next_endpoint_uuid + entries[device_uuid].append(entry_tuple) + else: + log.debug("ERROR in bidirectional connection 3") + log.debug("{}, {}, {}".format(i, next_device_uuid, device_uuid)) + return entries + #i+2 + next_2_endpoint = endpoints[i+2] + next_2_device_uuid, next_2_endpoint_uuid = next_2_endpoint[0:2] + #i+3 + next_3_endpoint = endpoints[i+3] + next_3_device_uuid, next_3_endpoint_uuid = next_3_endpoint[0:2] + if next_2_device_uuid == next_3_device_uuid and next_3_device_uuid == device_uuid: + entry_tuple = next_2_endpoint_uuid, next_3_endpoint_uuid + entries[device_uuid].append(entry_tuple) + i = i + 3 + else: + log.debug("ERROR in bidirection connection 4") + return {} + i = i + 1 + return entries + + def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]])->Dict: #entries = List[Tuple[str, str, str, Optional[str]]] @@ -35,7 +128,7 @@ def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]]) while(i < end): endpoint = endpoints[i] device_uuid, endpoint_uuid = endpoint[0:2] - log.debug("current OCTools step {}, {}, {}".format(i, device_uuid, endpoint_uuid)) + log.info("current OCTools step {}, {}, {}".format(i, device_uuid, endpoint_uuid)) if device_uuid not in entries.keys(): entries[device_uuid] = [] if i == 0: @@ -45,7 +138,7 @@ def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]]) next_device_uuid, next_endpoint_uuid = next_endpoint[0:2] if next_device_uuid == device_uuid: bidir = 1 - log.debug("connection is bidirectional") + log.info("connection is bidirectional") entry_tuple = next_endpoint_uuid, "0" entries[device_uuid].append(entry_tuple) i = i + 1 @@ -65,11 +158,11 @@ def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]]) entry_tuple = endpoint_uuid, next_endpoint_uuid entries[device_uuid].append(entry_tuple) i = i + 1 - log.debug("current OCTools step {}, {}, {}".format(i, next_device_uuid, device_uuid)) + log.info("current OCTools step {}, {}, {}".format(i, next_device_uuid, device_uuid)) else: log.debug("ERROR in unidirectional connection 4") return {} - if bidir: + else: log.debug("Ocheck i {}, {}, {}".format(i, i+1, end-1)) if i + 1 == end-1: log.debug("current OCTools step {}, {}, {}".format(i, device_uuid, endpoint_uuid)) @@ -105,6 +198,8 @@ def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]]) #i+3 next_3_endpoint = endpoints[i+3] next_3_device_uuid, next_3_endpoint_uuid = next_3_endpoint[0:2] + log.debug(f"de {device_uuid} and i {i}") + log.debug(f"de2 {next_2_device_uuid} and dev3 {next_3_device_uuid}") if next_2_device_uuid == next_3_device_uuid and next_3_device_uuid == device_uuid: entry_tuple = next_2_endpoint_uuid, next_3_endpoint_uuid entries[device_uuid].append(entry_tuple) @@ -116,6 +211,8 @@ def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]]) return entries + + def ob_flows(endpoints : List[Tuple[str, str, Optional[str]]], bidir : int): entries = {} end = len(endpoints) @@ -239,6 +336,7 @@ def conn_flows(endpoints : List[Tuple[str, str, Optional[str]]], bidir : int): i = i + 1 #if bidir reading 4 endpoints per node if bidir: + log.info(f"i starts with {i} ") i = i + 1 while(i < end-2): #i @@ -263,6 +361,8 @@ def conn_flows(endpoints : List[Tuple[str, str, Optional[str]]], bidir : int): #i+3 next_3_endpoint = endpoints[i+3] next_3_device_uuid, next_3_endpoint_uuid = next_3_endpoint[0:2] + log.info(f"dev {device_uuid} ") + log.info(f"dev2 {next_2_device_uuid} dev3 {next_3_device_uuid} ") if next_2_device_uuid == next_3_device_uuid and next_3_device_uuid == device_uuid: entry_tuple = next_2_endpoint_uuid, next_3_endpoint_uuid entries[device_uuid].append(entry_tuple) @@ -296,11 +396,14 @@ def conn_flows(endpoints : List[Tuple[str, str, Optional[str]]], bidir : int): entries[device_uuid].append(entry_tuple) return entries + + def endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]], bidir : int, is_ob: bool)->Dict: if is_ob: entries = ob_flows(endpoints, bidir) else: entries = conn_flows(endpoints, bidir) + return entries def get_device_endpint_name(endpoint_uuid : str, device_uuid : str, task_executor) -> Tuple: diff --git a/src/service/service/task_scheduler/TaskExecutor.py b/src/service/service/task_scheduler/TaskExecutor.py index 51b3cf00b949e15ca649d8ba14826136ad29fca0..ff97fd931ab0239a976e6ed96da79718e41df69e 100644 --- a/src/service/service/task_scheduler/TaskExecutor.py +++ b/src/service/service/task_scheduler/TaskExecutor.py @@ -145,7 +145,8 @@ class TaskExecutor: # New function Andrea for Optical Devices def configure_optical_device( - self, device : Device, settings : str, flows : list, is_opticalband : bool + self, device : Device, settings : str, flows : list, is_opticalband : bool, + connection_uuid:str ): device_key = get_device_key(device.device_id) optical_config_id = OpticalConfigId() @@ -160,6 +161,7 @@ class TaskExecutor: result = self._context_client.SelectOpticalConfig(optical_config_id) new_config = json.loads(result.config) + if is_opticalband : new_config['connection_uuid']=connection_uuid if 'type' in new_config: config_type=new_config['type'] if config_type == 'optical-transponder': @@ -177,7 +179,11 @@ class TaskExecutor: # Deconfiguring Optical Devices ( CNIT ) def deconfigure_optical_device( - self, device : Device, channel_indexes : list, is_opticalband : bool, dev_flow : list + self, device : Device + , channel_indexes : list + , is_opticalband : bool + , dev_flow : list + ,bidir=None ): errors = [] flows = [] @@ -202,6 +208,7 @@ class TaskExecutor: # for extractor in device service to extract the index , dummy data for freq and band required indexes["frequency"] = None indexes["band"] = None + indexes["bidir"]=bidir if result is not None: new_config = json.loads(result.config) new_config["new_config"]=indexes @@ -211,6 +218,7 @@ class TaskExecutor: # new_optical_config.config= json.dumps(new_config) # new_optical_config.opticalconfig_id.CopyFrom (optical_config_id) # new_optical_config.device_id.CopyFrom(device.device_id) + LOGGER.info(f"SSSSSSS->{result}") self._device_client.DisableOpticalDevice(result) except Exception as e: errors.append(e) @@ -332,6 +340,12 @@ class TaskExecutor: #else: # return devices return devices + + + def set_optical_band(self, connection : ConnectionId,device:DeviceId) -> None: + device_key = get_device_key(device.device_id) + self._device_client.ConfigureDevice(device) + self._store_grpc_object(CacheableObjectType.DEVICE, device_key, device) def get_device_type_drivers_for_connection( diff --git a/src/service/service/task_scheduler/TaskScheduler.py b/src/service/service/task_scheduler/TaskScheduler.py index b68b4839129e81ec94889cd1b651a1d901a3c117..5c777a811b42b7da63e03ac215e5266cff800bef 100644 --- a/src/service/service/task_scheduler/TaskScheduler.py +++ b/src/service/service/task_scheduler/TaskScheduler.py @@ -13,9 +13,11 @@ # limitations under the License. import graphlib, logging, queue, time +from common.method_wrappers.ServiceExceptions import NotFoundException from typing import TYPE_CHECKING, Dict, Tuple from common.proto.context_pb2 import ( - Connection, ConnectionId, Service, ServiceId, ServiceStatusEnum, ConnectionList + Connection, ConnectionId, Service, ServiceId, ServiceStatusEnum, ConnectionList, + OpticalBand ) from common.proto.pathcomp_pb2 import PathCompReply from common.tools.grpc.Tools import grpc_message_to_json_string @@ -23,7 +25,9 @@ from context.client.ContextClient import ContextClient from service.service.tools.ObjectKeys import get_connection_key, get_service_key from .tasks._Task import _Task from .tasks.Task_ConnectionConfigure import Task_ConnectionConfigure +from .tasks.Task_OpticalConnectionConfigure import Task_OpticalConnectionConfigure from .tasks.Task_OpticalConnectionDeconfigure import Task_OpticalConnectionDeconfigure + from .tasks.Task_OpticalServiceDelete import Task_OpticalServiceDelete from .tasks.Task_ConnectionDeconfigure import Task_ConnectionDeconfigure from .tasks.Task_ServiceDelete import Task_ServiceDelete @@ -85,6 +89,19 @@ class TasksScheduler: # deleting a service requires the service is in removing state self._dag.add(service_delete_key, service_removing_key) return service_removing_key, service_delete_key + + + def _optical_service_create(self, service_id : ServiceId , has_media_channel : bool + , has_optical_band = True) -> Tuple[str, str]: + service_planned_key = self._add_task_if_not_exists(Task_ServiceSetStatus( + self._executor, service_id, ServiceStatusEnum.SERVICESTATUS_PLANNED)) + + service_active_key = self._add_task_if_not_exists(Task_ServiceSetStatus( + self._executor, service_id, ServiceStatusEnum.SERVICESTATUS_ACTIVE)) + + # activating a service requires the service is in planning state + self._dag.add(service_active_key, service_planned_key) + return service_planned_key, service_active_key def _optical_service_remove( self, service_id : ServiceId, has_media_channel : bool, has_optical_band = True @@ -135,6 +152,29 @@ class TasksScheduler: self._dag.add(service_delete_key, connection_deconfigure_key) return connection_deconfigure_key + + + def _optical_connection_configure(self, connection_id : ConnectionId + , service_id : ServiceId , + has_media_channel : bool, has_optical_band = True) -> str: + optical_connection_configure_key = self._add_task_if_not_exists(Task_OpticalConnectionConfigure( + self._executor, connection_id)) + + + # the connection configuration depends on its connection's service being in planning state + service_planned_key = self._add_task_if_not_exists(Task_ServiceSetStatus( + self._executor, service_id, ServiceStatusEnum.SERVICESTATUS_PLANNED)) + self._dag.add(optical_connection_configure_key, service_planned_key) + + + + # the connection's service depends on the connection configuration to transition to active state + service_active_key = self._add_task_if_not_exists(Task_ServiceSetStatus( + self._executor, service_id, ServiceStatusEnum.SERVICESTATUS_ACTIVE)) + self._dag.add(service_active_key, optical_connection_configure_key) + + + return optical_connection_configure_key def _optical_connection_deconfigure( self, connection_id : ConnectionId, service_id : ServiceId, @@ -218,10 +258,118 @@ class TasksScheduler: else : has_optical_band = True return (has_media_channel, has_optical_band) + + + + def compose_from_opticalcontroller_reply( + self, pathcomp_reply : PathCompReply, is_delete : bool = False + ) -> None: + t0 = time.time() + include_service = self._optical_service_remove if is_delete else self._optical_service_create + include_connection = self._optical_connection_deconfigure if is_delete else self._optical_connection_configure - def compose_from_optical_service( - self, service : Service, params : dict, is_delete : bool = False + #pending_items_to_explore.put(service) + has_media_channel = None + has_optical_band = None + + for service in pathcomp_reply.services: + + connections = self._context_client.ListConnections(service.service_id) + has_media_channel, has_optical_band = self.check_service_for_media_channel( + connections=connections, item=service.service_id + ) + + + include_service(service.service_id , has_media_channel=has_media_channel, has_optical_band=has_optical_band) + self._add_service_to_executor_cache(service) + + for connection in connections.connections: + self._add_connection_to_executor_cache(connection) + + + + for connection in pathcomp_reply.connections: + + connection_key = include_connection( + connection.connection_id, connection.service_id, has_media_channel=has_media_channel, + has_optical_band=has_optical_band + ) + self._add_connection_to_executor_cache(connection) + + self._executor.get_service(connection.service_id) + for sub_service_id in connection.sub_service_ids: + _,service_key_done = include_service( + sub_service_id, has_media_channel=has_media_channel, + has_optical_band=has_optical_band + ) + self._executor.get_service(sub_service_id) + self._dag.add(connection_key, service_key_done) + + t1 = time.time() + LOGGER.debug('[compose_from_service] elapsed_time: {:f} sec'.format(t1-t0)) + + + + def compose_from_service_expansion( + self, service :Service, ) -> None: + t0 = time.time() + include_service = self._optical_service_create + include_connection = self._optical_connection_configure + + logging.debug(f"after setting the config {service}") + #pending_items_to_explore.put(service) + has_media_channel = None + has_optical_band = None + if service is None : raise NotFoundException('Service', service, extra_details=[ + 'service not found ' + ]) + + + + connections = self._context_client.ListConnections(service.service_id) + has_media_channel, has_optical_band = self.check_service_for_media_channel( + connections=connections, item=service.service_id + ) + + _,service_key_done= include_service(service.service_id , + has_media_channel=has_media_channel, + has_optical_band=has_optical_band) + # self._add_service_to_executor_cache(service) + service_updating_key = self._add_task_if_not_exists(Task_ServiceSetStatus( + self._executor, service.service_id, ServiceStatusEnum.SERVICESTATUS_UPDATING + )) + self._add_service_to_executor_cache(service) + for connection in connections.connections: + connection_key = include_connection( + connection.connection_id, connection.service_id, has_media_channel=has_media_channel, + has_optical_band=has_optical_band + ) + self._add_connection_to_executor_cache(connection) + self._dag.add(connection_key, service_updating_key) + + + # for connection in pathcomp_reply.connections: + + # connection_key = include_connection( + # connection.connection_id, connection.service_id, has_media_channel=has_media_channel, + # has_optical_band=has_optical_band + # ) + # self._add_connection_to_executor_cache(connection) + + # self._executor.get_service(connection.service_id) + # for sub_service_id in connection.sub_service_ids: + # _,service_key_done = include_service( + # sub_service_id, has_media_channel=has_media_channel, + # has_optical_band=has_optical_band + # ) + # self._executor.get_service(sub_service_id) + # self._dag.add(connection_key, service_key_done) + + t1 = time.time() + LOGGER.debug('[compose_from_service] elapsed_time: {:f} sec'.format(t1-t0)) + + def compose_from_optical_service(self, service : Service, params:dict, is_delete : bool = False) -> None: t0 = time.time() include_service = self._optical_service_remove if is_delete else self._service_create include_connection = self._optical_connection_deconfigure if is_delete else self._connection_configure @@ -230,103 +378,125 @@ class TasksScheduler: explored_items = set() pending_items_to_explore = queue.Queue() pending_items_to_explore.put(service) - has_media_channel = None - has_optical_band = None - reply = None - code = 0 - reply_not_allowed = "DELETE_NOT_ALLOWED" + has_media_channel=None + has_optical_band=None + reply=None + code=0 + reply_not_allowed="DELETE_NOT_ALLOWED" while not pending_items_to_explore.empty(): try: item = pending_items_to_explore.get(block=False) + except queue.Empty: break - + if isinstance(item, Service): + str_item_key = grpc_message_to_json_string(item.service_id) if str_item_key in explored_items: continue connections = self._context_client.ListConnections(item.service_id) - has_media_channel, has_optical_band = self.check_service_for_media_channel( - connections=connections, item=item.service_id - ) - + has_media_channel,has_optical_band=self.check_service_for_media_channel(connections=connections,item=item.service_id) + if len(service.service_config.config_rules) > 0: - reply, code = delete_lightpath( - params['src'], params ['dst'], params['bitrate'], params['ob_id'], - delete_band=not has_media_channel, flow_id= params['flow_id'] - ) - - if code == 400 and reply_not_allowed in reply: + + + reply,code = delete_lightpath( + params['src'] + ,params ['dst'] + , params['bitrate'] + , params['ob_id'] + ,delete_band=not has_media_channel + , flow_id= params['flow_id'] + ) + + + if code == 400 and reply_not_allowed in reply : MSG = 'Deleteion for the service is not Allowed , Served Lightpaths is not empty' raise Exception(MSG) - include_service( - item.service_id, has_media_channel=has_media_channel, has_optical_band=has_optical_band - ) + include_service(item.service_id,has_media_channel=has_media_channel,has_optical_band=has_optical_band) self._add_service_to_executor_cache(item) - + + for connection in connections.connections: - self._add_connection_to_executor_cache(connection) - pending_items_to_explore.put(connection) + self._add_connection_to_executor_cache(connection) + pending_items_to_explore.put(connection) explored_items.add(str_item_key) + elif isinstance(item, ServiceId): - if code == 400 and reply_not_allowed in reply: break + + if code == 400 and reply_not_allowed in reply:break + str_item_key = grpc_message_to_json_string(item) if str_item_key in explored_items: continue connections = self._context_client.ListConnections(item) - has_media_channel, has_optical_band = self.check_service_for_media_channel( - connections=connections, item=item - ) - include_service( - item, has_media_channel=has_media_channel, has_optical_band=has_optical_band - ) - self._executor.get_service(item) + has_media_channel,has_optical_band=self.check_service_for_media_channel(connections=connections,item=item) + + include_service(item,has_media_channel=has_media_channel,has_optical_band=has_optical_band) + + + self._executor.get_service(item) + for connection in connections.connections: + self._add_connection_to_executor_cache(connection) pending_items_to_explore.put(connection) + + + + explored_items.add(str_item_key) elif isinstance(item, Connection): + + if code == 400 and reply_not_allowed in reply:break + str_item_key = grpc_message_to_json_string(item.connection_id) if str_item_key in explored_items: continue - connection_key = include_connection( - item.connection_id, item.service_id, has_media_channel=has_media_channel, - has_optical_band=has_optical_band - ) + + + connection_key = include_connection(item.connection_id, item.service_id,has_media_channel=has_media_channel,has_optical_band=has_optical_band) self._add_connection_to_executor_cache(connection) - + if include_service_config is not None : - connections_list = ConnectionList() - connections_list.connections.append(item) - is_media_channel, _ = self.check_service_for_media_channel( - connections=connections_list, item=service - ) - if has_optical_band and is_media_channel: - include_service_config( - item.connection_id, item.service_id - ) + connections_list = ConnectionList() + connections_list.connections.append(item) + + is_media_channel,_=self.check_service_for_media_channel(connections=connections_list,item=service) + + if has_optical_band and is_media_channel: + include_service_config(item.connection_id + , item.service_id + + ) + + self._executor.get_service(item.service_id) pending_items_to_explore.put(item.service_id) - + + for sub_service_id in item.sub_service_ids: - _,service_key_done = include_service( - sub_service_id, has_media_channel=has_media_channel, - has_optical_band=has_optical_band - ) + _,service_key_done = include_service(sub_service_id,has_media_channel=has_media_channel,has_optical_band=has_optical_band) self._executor.get_service(sub_service_id) self._dag.add(service_key_done, connection_key) pending_items_to_explore.put(sub_service_id) + + explored_items.add(str_item_key) + + else: MSG = 'Unsupported item {:s}({:s})' raise Exception(MSG.format(type(item).__name__, grpc_message_to_json_string(item))) - + t1 = time.time() LOGGER.debug('[compose_from_service] elapsed_time: {:f} sec'.format(t1-t0)) + def compose_from_service(self, service : Service, is_delete : bool = False) -> None: t0 = time.time() include_service = self._service_remove if is_delete else self._service_create diff --git a/src/service/service/task_scheduler/tasks/Task_OpticalConnectionConfigure.py b/src/service/service/task_scheduler/tasks/Task_OpticalConnectionConfigure.py new file mode 100644 index 0000000000000000000000000000000000000000..8a991326c453c625f174ff8e7125f21f338a4244 --- /dev/null +++ b/src/service/service/task_scheduler/tasks/Task_OpticalConnectionConfigure.py @@ -0,0 +1,88 @@ +# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import TYPE_CHECKING, Dict, Tuple +from common.DeviceTypes import DeviceTypeEnum +from common.method_wrappers.ServiceExceptions import OperationFailedException +from common.proto.context_pb2 import ConnectionId, Device +from common.tools.grpc.Tools import grpc_message_to_json_string +from service.service.service_handler_api.Tools import check_errors_setendpoint +from service.service.task_scheduler.TaskExecutor import TaskExecutor +from service.service.tools.EndpointIdFormatters import endpointids_to_raw +from service.service.tools.ObjectKeys import get_connection_key + +from ._Task import _Task +import logging + +if TYPE_CHECKING: + from service.service.service_handler_api._ServiceHandler import _ServiceHandler + +KEY_TEMPLATE = 'optical_Connection ({connection_id:s}):configure' + +class Task_OpticalConnectionConfigure(_Task): + def __init__(self, task_executor : TaskExecutor, connection_id : ConnectionId) -> None: + super().__init__(task_executor) + self._connection_id = connection_id + + @property + def connection_id(self) -> ConnectionId: return self._connection_id + + @staticmethod + def build_key(connection_id : ConnectionId) -> str: # pylint: disable=arguments-differ + str_connection_id = get_connection_key(connection_id) + return KEY_TEMPLATE.format(connection_id=str_connection_id) + + @property + def key(self) -> str: return self.build_key(self._connection_id) + + def execute(self) -> None: + connection = self._task_executor.get_connection(self._connection_id) + service = self._task_executor.get_service(connection.service_id) + + service_handler_settings = {} + service_handler=None + service_handlers = self._task_executor.get_service_handlers(connection, service, **service_handler_settings) + for _, (handler, connection_devices) in service_handlers.items(): + if service_handler is None : service_handler=handler + else : + if type(handler) != type(service_handler) : + raise Exception("Devices are not compatible ") + + connection_uuid = connection.connection_id.connection_uuid.uuid + endpointids_to_set = endpointids_to_raw(connection.path_hops_endpoint_ids) + errors = list() + + connection_uuid = connection.connection_id.connection_uuid.uuid + results_setendpoint = service_handler.SetEndpoint(endpointids_to_set, connection_uuid=connection_uuid) + errors.extend(check_errors_setendpoint(endpointids_to_set, results_setendpoint)) + + if len(errors) > 0: + MSG = 'SetEndpoint for Connection({:s}) from Service({:s})' + str_connection = grpc_message_to_json_string(connection) + str_service = grpc_message_to_json_string(service) + raise OperationFailedException(MSG.format(str_connection, str_service), extra_details=errors) + + self._task_executor.set_connection(connection) + + results_setendOpticalConfigs = service_handler.SetOpticalConfig( + endpointids_to_set, connection_uuid=connection_uuid + ) + errors.extend(check_errors_setendpoint(endpointids_to_set, results_setendOpticalConfigs)) + + if len(errors) > 0: + MSG = 'SetOpticalConfigs for Optical Connection({:s}) from Optical Service({:s})' + str_connection = grpc_message_to_json_string(connection) + str_service = grpc_message_to_json_string(service) + raise OperationFailedException(MSG.format(str_connection, str_service), extra_details=errors) + diff --git a/src/service/service/tools/OpticalTools.py b/src/service/service/tools/OpticalTools.py index 8364d8a95c711168d19551e45c17b50f8e499165..bc5ea725875992c5a661ebbf34841c2e1656815a 100644 --- a/src/service/service/tools/OpticalTools.py +++ b/src/service/service/tools/OpticalTools.py @@ -13,12 +13,16 @@ # limitations under the License. # +from common.method_wrappers.ServiceExceptions import NotFoundException +from service.service.service_handler_api.SettingsHandler import SettingsHandler import functools, json, logging, requests, uuid from typing import List -from common.Constants import ServiceNameEnum +from context.client.ContextClient import ContextClient +from common.Constants import ServiceNameEnum +from common.tools.context_queries.OpticalConfig import ( find_optical_band) from common.proto.context_pb2 import( Device, DeviceId, Service, Connection, EndPointId, TopologyId, ContextId, Uuid, - ConfigRule, ConfigActionEnum, ConfigRule_Custom + ConfigRule, ConfigActionEnum, ConfigRule_Custom, Empty,OpticalBandId,OpticalBand ) from common.proto.pathcomp_pb2 import PathCompReply from common.Settings import ( @@ -26,7 +30,10 @@ from common.Settings import ( find_environment_variables, get_env_var_name ) from service.service.tools.replies import ( - reply_uni_txt, optical_band_uni_txt, reply_bid_txt, optical_band_bid_txt + reply_uni_txt + , optical_band_uni_txt + , reply_bid_txt + , optical_band_bid_txt ) log = logging.getLogger(__name__) @@ -110,7 +117,7 @@ def refresh_opticalcontroller(topology_id : dict): urlx = "{:s}/GetTopology/{:s}/{:s}".format(base_url, cxt_id_str, topo_id_str) res = requests.get(urlx, headers=headers) if res is not None: - log.debug(f"DELETELIGHTPATH Response {res}") + log.debug(f"GetTopology Response {res}") def add_lightpath(src, dst, bitrate, bidir, ob_band) -> str: @@ -160,7 +167,9 @@ def delete_lightpath( src, dst, bitrate, ob_id, delete_band, flow_id=None) -> st if flow_id is not None: urlx = "{:s}/DelFlexLightpath/{}/{}/{}/{}/{}".format(base_url, src, dst, bitrate, ob_id, flow_id) else : - urlx = "{:s}/DelOpticalBand/{}/{}/{}".format(base_url, src, dst, ob_id) + #urlx = "http://{}:{}/OpticalTFS/DelOpticalBand/{}/{}/{}".format(OPTICAL_IP, OPTICAL_PORT, src, dst, ob_id) + urlx = "{:s}/DelOpticalBandSimple/{}".format(base_url, ob_id) + headers = {"Content-Type": "application/json"} r = requests.delete(urlx, headers=headers) reply = r.text @@ -211,7 +220,7 @@ def adapt_reply(devices, service, reply_json, context_id, topology_id, optical_b uuuid_x = str(uuid.uuid4()) connection_ob.connection_id.connection_uuid.uuid = uuuid_x connection_ob.service_id.CopyFrom(service.service_id) - + new_ob = r["new_optical_band"] if 'new_optical_band' in r else None ob_id = ob["optical_band_id"] obt = ob["band_type"] if obt == "l_slots": @@ -237,49 +246,51 @@ def adapt_reply(devices, service, reply_json, context_id, topology_id, optical_b } rules_ob.append(ConfigRule_Custom(resource_key="/settings-ob_{}".format(uuuid_x), resource_value=json.dumps(val_ob))) bidir_ob = ob["bidir"] - for devxb in ob["flows"].keys(): - log.debug("optical-band device {}".format(devxb)) - in_end_point_b = "0" - out_end_point_b = "0" - in_end_point_f = ob["flows"][devxb]["f"]["in"] - out_end_point_f = ob["flows"][devxb]["f"]["out"] - log.debug("optical-band ports {}, {}".format(in_end_point_f, out_end_point_f)) - if bidir_ob: - in_end_point_b = ob["flows"][devxb]["b"]["in"] - out_end_point_b = ob["flows"][devxb]["b"]["out"] - log.debug("optical-band ports {}, {}".format(in_end_point_b, out_end_point_b)) - #if (in_end_point_f == "0" or out_end_point_f == "0") and (in_end_point_b == "0" or out_end_point_b == "0"): - if in_end_point_f != "0": - d_ob, p_ob = get_uuids_from_names(devices, devxb, in_end_point_f) - if d_ob != "" and p_ob != "": - end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) - connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) - else: - log.info("no map device port for device {} port {}".format(devxb, in_end_point_f)) - - if out_end_point_f != "0": - d_ob, p_ob = get_uuids_from_names(devices, devxb, out_end_point_f) - if d_ob != "" and p_ob != "": - end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) - connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) - else: - log.info("no map device port for device {} port {}".format(devxb, out_end_point_f)) - if in_end_point_b != "0": - d_ob, p_ob = get_uuids_from_names(devices, devxb, in_end_point_b) - if d_ob != "" and p_ob != "": - end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) - connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) - else: - log.info("no map device port for device {} port {}".format(devxb, in_end_point_b)) - if out_end_point_b != "0": - d_ob, p_ob = get_uuids_from_names(devices, devxb, out_end_point_b) - if d_ob != "" and p_ob != "": - end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) - connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) - else: - log.info("no map device port for device {} port {}".format(devxb, out_end_point_b)) - log.debug("optical-band connection {}".format(connection_ob)) - + # in case the service is built upon existed optical band , don't clacluate the endpoints of it + if new_ob != 2 : + for devxb in ob["flows"].keys(): + log.debug("optical-band device {}".format(devxb)) + in_end_point_b = "0" + out_end_point_b = "0" + in_end_point_f = ob["flows"][devxb]["f"]["in"] + out_end_point_f = ob["flows"][devxb]["f"]["out"] + log.debug("optical-band ports {}, {}".format(in_end_point_f, out_end_point_f)) + if bidir_ob: + in_end_point_b = ob["flows"][devxb]["b"]["in"] + out_end_point_b = ob["flows"][devxb]["b"]["out"] + log.debug("optical-band ports {}, {}".format(in_end_point_b, out_end_point_b)) + #if (in_end_point_f == "0" or out_end_point_f == "0") and (in_end_point_b == "0" or out_end_point_b == "0"): + if in_end_point_f != "0": + d_ob, p_ob = get_uuids_from_names(devices, devxb, in_end_point_f) + if d_ob != "" and p_ob != "": + end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) + connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) + else: + log.info("no map device port for device {} port {}".format(devxb, in_end_point_f)) + + if out_end_point_f != "0": + d_ob, p_ob = get_uuids_from_names(devices, devxb, out_end_point_f) + if d_ob != "" and p_ob != "": + end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) + connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) + else: + log.info("no map device port for device {} port {}".format(devxb, out_end_point_f)) + if in_end_point_b != "0": + d_ob, p_ob = get_uuids_from_names(devices, devxb, in_end_point_b) + if d_ob != "" and p_ob != "": + end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) + connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) + else: + log.info("no map device port for device {} port {}".format(devxb, in_end_point_b)) + if out_end_point_b != "0": + d_ob, p_ob = get_uuids_from_names(devices, devxb, out_end_point_b) + if d_ob != "" and p_ob != "": + end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) + connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) + else: + log.info("no map device port for device {} port {}".format(devxb, out_end_point_b)) + log.debug("optical-band connection {}".format(connection_ob)) + connection_f = add_connection_to_reply(opt_reply) connection_f.connection_id.connection_uuid.uuid = str(uuid.uuid4()) connection_f.service_id.CopyFrom(service.service_id) @@ -351,13 +362,16 @@ def adapt_reply(devices, service, reply_json, context_id, topology_id, optical_b service.service_config.config_rules.add().CopyFrom(rule) if len(rules_ob) > 0: - for rulex in rules_ob: - rule_ob = ConfigRule(action=ConfigActionEnum.CONFIGACTION_SET, custom=rulex) - service.service_config.config_rules.add().CopyFrom(rule_ob) + if new_ob != 2 : + for rulex in rules_ob: + rule_ob = ConfigRule(action=ConfigActionEnum.CONFIGACTION_SET, custom=rulex) + service.service_config.config_rules.add().CopyFrom(rule_ob) opt_reply.services.add().CopyFrom(service) return opt_reply + + def add_service_to_reply(reply : PathCompReply, service : Service) -> Service: service_x = reply.services.add() service_x.CopyFrom(service) @@ -366,3 +380,64 @@ def add_service_to_reply(reply : PathCompReply, service : Service) -> Service: def add_connection_to_reply(reply : PathCompReply) -> Connection: conn = reply.connections.add() return conn + + + + +def update_config_rules (service:Service,config_to_update:dict): + config_rules = service.service_config.config_rules + if len(config_rules) == 0 : return service + for key,new_value in config_to_update.items(): + for c in config_rules: + if c.custom.resource_key == key : + c.custom.resource_value = json.dumps(new_value) + + + return service + + + + +def extend_optical_band (reply,optical_band_text)->Service : + logging.debug(f"optical-band extended {reply}") + logging.debug(f"optical-band_text {optical_band_text}") + optical_band_res= json.loads(optical_band_text) + if 'optical_band_id' not in optical_band_res: raise KeyError(f"opticalband id not found in the reply") + ob_index =optical_band_res['optical_band_id'] + band=optical_band_res['band'] + frequency=optical_band_res['freq'] + opticalband=find_optical_band(ob_index=ob_index) + if opticalband is None : + raise NotFoundException(f"Optical Band is not found ",extra_details=[ + f"The requested opticla band for index {ob_index} is not found" + ]) + + service = opticalband.service + connection_uuid = opticalband.connection_id.connection_uuid.uuid + + setting_handler = SettingsHandler(service.service_config) + config_to_update = {} + setting_key = '/settings-ob_{}'.format(connection_uuid) + config = setting_handler.get(setting_key) + + + config.value['band']=band + config.value['frequency']=frequency + config.value['low-freq']= int(frequency - (band/2)) + config.value['up-freq']= int(frequency + (band/2)) + + logging.debug(f"before setting the config {service}") + config_to_update[setting_key]=config.value + setting_key = '/settings' + config = setting_handler.get(setting_key) + config.value['ob-expanded']=1 + config_to_update[setting_key]=config.value + logging.debug(f"config_to_update {config_to_update}") + service = update_config_rules(service,config_to_update) + + + return service + + + + diff --git a/src/tests/ofc24/r_t.sh b/src/tests/ofc24/r_t.sh index 5c931d4d1d4e11ccec11abc566ce41dc866b5b01..ec0d57aea38d9b07c5d369186aa332fac4b5aff5 100755 --- a/src/tests/ofc24/r_t.sh +++ b/src/tests/ofc24/r_t.sh @@ -25,7 +25,9 @@ docker rm na3 docker rm t2 docker rm na2 -screen -dmS t1 -T xterm sh -c "docker run --name t1 -p 10.0.2.4:2023:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/oc23bgp.img:latest bash -c 'cp /files/transponders_x4.xml demoECOC21.xml ; ./startNetconfAgent.sh'" -screen -dmS t3 -T xterm sh -c "docker run --name na3 -p 10.0.2.4:2025:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/flexscale-node.img:latest bash -c 'cp /files/platform_r1.xml init_openconfig-platform.xml ; ./startNetconfAgent.sh'" -screen -dmS t2 -T xterm sh -c "docker run --name t2 -p 10.0.2.4:2024:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/oc23bgp.img:latest bash -c 'cp /files/transponders_x4_2.xml demoECOC21.xml ; ./startNetconfAgent.sh'" -screen -dmS t4 -T xterm sh -c "docker run --name na2 -p 10.0.2.4:2026:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/flexscale-node.img:latest bash -c 'cp /files/platform_r2.xml init_openconfig-platform.xml ; ./startNetconfAgent.sh'" +docker network create --subnet=192.168.100.0/24 my-custom-network + +screen -dmS t1 -T xterm sh -c "docker run --name t1 --net my-custom-network -p 10.0.2.4:2023:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/oc23bgp.img:latest bash -c 'cp /files/transponders_x4.xml demoECOC21.xml ; ./startNetconfAgent.sh'" +screen -dmS t3 -T xterm sh -c "docker run --name na3 --net my-custom-network -p 10.0.2.4:2025:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/flexscale-node.img:latest bash -c 'cp /files/platform_r1.xml init_openconfig-platform.xml ; ./startNetconfAgent.sh'" +screen -dmS t2 -T xterm sh -c "docker run --name t2 --net my-custom-network -p 10.0.2.4:2024:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/oc23bgp.img:latest bash -c 'cp /files/transponders_x4_2.xml demoECOC21.xml ; ./startNetconfAgent.sh'" +screen -dmS t4 -T xterm sh -c "docker run --name na2 --net my-custom-network -p 10.0.2.4:2026:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/flexscale-node.img:latest bash -c 'cp /files/platform_r2.xml init_openconfig-platform.xml ; ./startNetconfAgent.sh'" diff --git a/src/tests/ofc24/refresh_op.sh b/src/tests/ofc24/refresh_op.sh new file mode 100644 index 0000000000000000000000000000000000000000..3a8fca7f9d3a8d9568b62a309b62d941b887704b --- /dev/null +++ b/src/tests/ofc24/refresh_op.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +ip=$(sudo kubectl get all --all-namespaces | grep service/opticalcontrollerservice | awk '{print $4}') + +#echo $ip + +push=$(curl -X GET "http://$ip:10060/OpticalTFS/GetTopology/admin/admin") + + +links=$(curl -X GET "http://$ip:10060/OpticalTFS/GetLinks") + +echo $links diff --git a/src/tests/ofc24/roadms.sh b/src/tests/ofc24/roadms.sh index f3708b1365668db53f55d413805300bc1b4c4a5e..66fa2a1d2d1e659b1eaf51c670302182e904e2ad 100644 --- a/src/tests/ofc24/roadms.sh +++ b/src/tests/ofc24/roadms.sh @@ -23,4 +23,4 @@ docker rm na4 screen -dmS t3 -T xterm sh -c "docker run --name na3 -p 10.0.2.4:2025:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/flexscale-node.img:latest bash -c 'cp /files/platform_r1.xml demoECOC21.xml ; ./startNetconfAgent.sh'" -screen -dmS t4 -T xterm sh -c "docker run --name na4 -p 10.0.2.4:2026:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/flexscale-node.img:latest bash -c 'cp /files/platform_r2.xml demoECOC21.xml ; ./startNetconfAgent.sh'" \ No newline at end of file +#screen -dmS t4 -T xterm sh -c "docker run --name na4 -p 10.0.2.4:2026:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/flexscale-node.img:latest bash -c 'cp /files/platform_r2.xml demoECOC21.xml ; ./startNetconfAgent.sh'" diff --git a/src/tests/ofc24/transponders.sh b/src/tests/ofc24/transponders.sh old mode 100644 new mode 100755 index cfbb2fca5737b79d1f1e81e26bac893746baeb36..5125dcb952402d6cbc28f0ec1608bf336e599ad4 --- a/src/tests/ofc24/transponders.sh +++ b/src/tests/ofc24/transponders.sh @@ -22,5 +22,5 @@ docker rm tna2 -screen -dmS tt1 -T xterm sh -c "docker run --name tna1 -p 10.0.2.4:2023:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/oc23bgp.img:latest bash -c 'cp /files/platform_t1.xml demoECOC21.xml ; ./startNetconfAgent.sh'" -screen -dmS tt2 -T xterm sh -c "docker run --name tna2 -p 10.0.2.4:2024:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/oc23bgp.img:latest bash -c 'cp /files/platform_t2.xml demoECOC21.xml ; ./startNetconfAgent.sh'" +screen -dmS t1 -T xterm sh -c "docker run --name tna1 -p 10.0.2.4:2023:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/oc23bgp.img:latest bash -c 'cp /files/platform_t1.xml demoECOC21.xml ; ./startNetconfAgent.sh'" +#screen -dmS tt2 -T xterm sh -c "docker run --name tna2 -p 10.0.2.4:2024:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/oc23bgp.img:latest bash -c 'cp /files/platform_t2.xml demoECOC21.xml ; ./startNetconfAgent.sh'" diff --git a/src/webui/requirements.in b/src/webui/requirements.in index 8da0e74b6d269abde797a9b9460f34cf04213ae9..3b6c1087a71e565630c6c6a03066cd5d4e62fd65 100644 --- a/src/webui/requirements.in +++ b/src/webui/requirements.in @@ -17,4 +17,5 @@ Flask-WTF<2 flask-healthz<2 flask-unittest==0.1.3 lorem-text==2.1 +requests==2.27.1 werkzeug==2.3.7 diff --git a/src/webui/service/__main__.py b/src/webui/service/__main__.py index c044a17c300fa7ea530b34da1af4050a7941d6dd..99c06000f3c064cb75edca547dc9f7dc9b2134de 100644 --- a/src/webui/service/__main__.py +++ b/src/webui/service/__main__.py @@ -16,8 +16,10 @@ import hashlib, sys, logging from prometheus_client import start_http_server from common.Constants import ServiceNameEnum from common.Settings import ( - ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name, get_log_level, get_metrics_port, - get_service_baseurl_http, get_service_port_http, get_setting, wait_for_environment_variables) + ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name, + get_log_level, get_metrics_port, get_service_baseurl_http, get_service_port_http, + get_setting, wait_for_environment_variables +) from webui.service import create_app from webui.Config import MAX_CONTENT_LENGTH, HOST, SECRET_KEY, DEBUG @@ -33,7 +35,6 @@ def main(): logging.basicConfig(level=log_level) logger = logging.getLogger(__name__) - # DEPENDENCY QKD wait_for_environment_variables([ get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_HOST ), get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_PORT_GRPC), @@ -58,7 +59,9 @@ def main(): 'SECRET_KEY': SECRET_KEY, 'MAX_CONTENT_LENGTH': MAX_CONTENT_LENGTH, 'SESSION_COOKIE_NAME': create_unique_session_cookie_name(), + 'WTF_CSRF_ENABLED':False, }, web_app_root=web_app_root) + app.run(host=host, port=service_port, debug=debug) logger.info('Bye') diff --git a/src/webui/service/main/routes.py b/src/webui/service/main/routes.py index 66a114f230e5d1e65b4b6fb1e1b1ce76ee85c722..634080374ef4ac5418de1de95c25813f68743056 100644 --- a/src/webui/service/main/routes.py +++ b/src/webui/service/main/routes.py @@ -110,6 +110,7 @@ def home(): if descriptor_form.validate_on_submit(): process_descriptors(descriptor_form.descriptors) return redirect(url_for("main.home")) + except Exception as e: # pylint: disable=broad-except LOGGER.exception('Descriptor load failed') flash(f'Descriptor load failed: `{str(e)}`', 'danger') diff --git a/src/webui/service/optical_link/routes.py b/src/webui/service/optical_link/routes.py index b8e1264c21cef6bcb04f2934606c91df3e13ae6b..242573e2dcf839fe9bba56aa7bea37c1abab30db 100644 --- a/src/webui/service/optical_link/routes.py +++ b/src/webui/service/optical_link/routes.py @@ -14,10 +14,33 @@ from flask import current_app, render_template, Blueprint, flash, session, redirect, url_for -from common.proto.context_pb2 import Empty, OpticalLink, LinkId, OpticalLinkList +from common.proto.context_pb2 import Empty, OpticalLink, LinkId, OpticalLinkList , Service from common.tools.context_queries.EndPoint import get_endpoint_names from common.tools.context_queries.Topology import get_topology from context.client.ContextClient import ContextClient +import functools ,requests ,logging +from common.Constants import ServiceNameEnum +from common.tools.context_queries.OpticalConfig import find_optical_band +from common.Settings import ( + ENVVAR_SUFIX_SERVICE_BASEURL_HTTP, ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, + find_environment_variables, get_env_var_name +) + +get_optical_controller_setting = functools.partial(get_env_var_name, ServiceNameEnum.OPTICALCONTROLLER) +VAR_NAME_OPTICAL_CTRL_HOST = get_optical_controller_setting(ENVVAR_SUFIX_SERVICE_HOST) +VAR_NAME_OPTICAL_CTRL_PORT = get_optical_controller_setting(ENVVAR_SUFIX_SERVICE_PORT_GRPC) +VAR_NAME_OPTICAL_CTRL_BASEURL_HTTP = get_optical_controller_setting(ENVVAR_SUFIX_SERVICE_BASEURL_HTTP) +VAR_NAME_OPTICAL_CTRL_SCHEMA = get_optical_controller_setting('SCHEMA') + +settings = find_environment_variables([ + VAR_NAME_OPTICAL_CTRL_BASEURL_HTTP, + VAR_NAME_OPTICAL_CTRL_SCHEMA, + VAR_NAME_OPTICAL_CTRL_HOST, + VAR_NAME_OPTICAL_CTRL_PORT, + ]) + +host = settings.get(VAR_NAME_OPTICAL_CTRL_HOST) +port = settings.get(VAR_NAME_OPTICAL_CTRL_PORT, 80) optical_link = Blueprint('optical_link', __name__, url_prefix='/optical_link') context_client = ContextClient() @@ -106,3 +129,89 @@ def delete_all(): except Exception as e: flash(f"Problem in delete all optical link => {e}",'danger') return redirect(url_for('optical_link.home')) + + +@optical_link.route('getopticallinks', methods=(['GET'])) +def get_optical_links (): + + + urlx = "http://{:s}:{:s}/OpticalTFS/GetLinks".format(host,port) + + headers = {"Content-Type": "application/json"} + optical_links =[] + try: + r = requests.get(urlx, headers=headers) + reply = r.json() + if (reply and 'optical_links' in reply): + optical_links = reply["optical_links"] + for link in optical_links : + for k,v in link['optical_details'].items(): + if k == 'c_slots' or k =='l_slots' or k =='s_slots': + sorted_keys = sorted(v.keys(), key=lambda x: int(x), reverse=False) + sorted_dict = {key: v[key] for key in sorted_keys} + link['optical_details'][k] = sorted_dict + + except Exception as e : + logging.info(f"error {e}") + finally: + + return render_template( + 'opticalconfig/opticallinks.html', optical_links=optical_links + ) + + + + +@optical_link.route('getopticalbands', methods=(['GET'])) +def get_optical_bands(): + + + urlx = "http://{:s}:{:s}/OpticalTFS/GetOpticalBands".format(host,port) + + headers = {"Content-Type": "application/json"} + optical_bands ={} + service_uuid = None + try: + r = requests.get(urlx, headers=headers) + reply = r.json() + if (reply): + optical_bands=reply + ob_keys = optical_bands.keys() + for ob_key in ob_keys : + ob_service = find_optical_band(ob_key) + if ob_service is not None : + service_uuid=ob_service.service.service_id.service_uuid.uuid + optical_bands[ob_key]['service_uuid']=service_uuid + + logging.info(f"optical bands {optical_bands}") + + except Exception as e : + logging.info(f"error {e}") + finally: + + return render_template( + 'opticalconfig/opticalbands.html',optical_bands=optical_bands + ) + +@optical_link.route('getlightpath', methods=(['GET'])) +def get_lightpath(): + + + urlx = "http://{:s}:{:s}/OpticalTFS/GetLightpaths".format(host,port) + + headers = {"Content-Type": "application/json"} + light_paths ={} + try: + r = requests.get(urlx, headers=headers) + reply = r.json() + if (reply):light_paths=reply + + + logging.info(f"lightpaths {reply}") + except Exception as e : + logging.info(f"error {e}") + finally: + + return render_template( + 'opticalconfig/lightpaths.html',light_paths=light_paths + ) diff --git a/src/webui/service/opticalconfig/routes.py b/src/webui/service/opticalconfig/routes.py index b1bcfc7f0bb676a29c56fe12bd045570dc8f59a9..042cb63dd701306db835e96910215bfc78a2016d 100644 --- a/src/webui/service/opticalconfig/routes.py +++ b/src/webui/service/opticalconfig/routes.py @@ -12,15 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json, logging +import json, logging , time from flask import ( request, redirect, render_template, Blueprint, flash, session, url_for, current_app, make_response ) from common.proto.context_pb2 import ( - Empty, OpticalConfig, OpticalConfigId, OpticalConfigList + Empty, OpticalConfig, OpticalConfigId, OpticalConfigList , TopologyId ) +from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.context_queries.OpticalConfig import opticalconfig_get_uuid +from common.tools.object_factory.Context import json_context_id +from common.tools.object_factory.Topology import json_topology_id from common.DeviceTypes import DeviceTypeEnum from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient @@ -43,10 +46,11 @@ DESCRIPTOR_LOADER_NUM_WORKERS = 10 def home() : list_config = [] if 'context_uuid' not in session or 'topology_uuid' not in session: + #time.sleep(5) flash("Please select a context!", "warning") + return redirect(url_for("main.home")) - context_uuid = session['context_uuid'] - topology_uuid = session['topology_uuid'] + context_client.connect() opticalConfig_list : OpticalConfigList = context_client.GetOpticalConfig(Empty()) @@ -154,6 +158,57 @@ def delete_opitcalconfig (opticalconfig_uuid) : return redirect(url_for('opticalconfig.home')) +@opticalconfig.route('delete_all', methods=['GET']) +def delete_all_devices () : + try : + context_uuid = session['context_uuid'] + topology_uuid = session['topology_uuid'] + json_topo_id = json_topology_id(topology_uuid, context_id=json_context_id(context_uuid)) + context_client.connect() + response = context_client.GetTopologyDetails(TopologyId(**json_topo_id)) + context_client.close() + + devices = [] + for device in response.devices: + devices.append({ + 'device_id': device.device_id, + 'name': device.name, + 'type': device.device_type, + }) + + + optical_links = [] + for link in response.optical_links: + if len(link.link_endpoint_ids) != 2: + str_link = grpc_message_to_json_string(link) + LOGGER.warning('Unexpected link with len(endpoints) != 2: {:s}'.format(str_link)) + continue + optical_links.append({ + 'id': link.link_id.link_uuid.uuid, + 'name': link.name, + 'source': link.link_endpoint_ids[0].device_id.device_uuid.uuid, + 'target': link.link_endpoint_ids[1].device_id.device_uuid.uuid, + }) + if len(optical_links)!= 0 : + flash(f'you should delete all optical links before deleting all the devices ', 'danger') + return redirect(url_for('opticalconfig.home')) + if len(devices) > 0 : + LOGGER.info(f'devices to delete {devices}') + device_client.connect() + for d in devices: + if 'device_id' in d : + device_client.DeleteDevice(d['device_id']) + + flash('All devices were deleted successfully','success') + + + except Exception as e: # pylint: disable=broad-except + flash(f'Problem deleting optical devices ', 'danger') + current_app.logger.exception(e) + return redirect(url_for('opticalconfig.home')) + + + @opticalconfig.route('/update_opticalconfig', methods=['POST']) def update_externally(): if (request.method == 'POST'): diff --git a/src/webui/service/templates/base.html b/src/webui/service/templates/base.html index 8b7a6d90a514fb372cd1808653d09c737de0a940..c82e92290f631a2a0a4bbe60a877906bae5b491b 100644 --- a/src/webui/service/templates/base.html +++ b/src/webui/service/templates/base.html @@ -90,9 +90,9 @@ {% if is_deployed_optical() %} {% endif %} diff --git a/src/webui/service/templates/base_optical/home.html b/src/webui/service/templates/base_optical/home.html index d3a4ef4c6cb788635dbb1f356b251d607aca1877..bfa9f674c598e7b254e55cb4e9e47acd96558cc5 100644 --- a/src/webui/service/templates/base_optical/home.html +++ b/src/webui/service/templates/base_optical/home.html @@ -17,20 +17,60 @@ {% extends 'base.html' %} {% block content %} -

Optical Configurations

+

Optical View

-
- - - Optical Devices - +
+
Optical Configuration :
+
+ {% endblock %} diff --git a/src/webui/service/templates/optical_link/detail.html b/src/webui/service/templates/optical_link/detail.html index 0c4353ea11cadecdb90dd211848e1936156ed5b1..e9027c14f53e65fa8c157a3aa65f1b8b197e1fc5 100644 --- a/src/webui/service/templates/optical_link/detail.html +++ b/src/webui/service/templates/optical_link/detail.html @@ -17,7 +17,6 @@ {% extends 'base.html' %} {% block content %} -

Link {{ link.name }} ({{ link.link_id.link_uuid.uuid }})

-
- - -
+
+{% for link in optical_links %} +

Link : {{ link.name }}

+
UUID: {{ link.link_id.link_uuid.uuid }}
- Name: {{ link.name }}
+
- - - + + + @@ -56,21 +52,12 @@ + - - + {% endfor %} @@ -89,28 +76,20 @@ - {% for field_descriptor, field_value in link.optical_details.ListFields() %} - {% if field_descriptor.name != "c_slots" and field_descriptor.name != "s_slots" and field_descriptor.name != "l_slots" %} + {% for field_descriptor, field_value in link.optical_details.items() %} + + - {%endif%} + {% endfor %} - {%if c_slots %} - - - - - {%endif%} + {%if l_slots %}
Endpoint UUIDNameDeviceEndpoint TypeDevice UUID
{{ endpoint.endpoint_uuid.uuid }} - {{ endpoints_data.get(endpoint.endpoint_uuid.uuid, (endpoint.endpoint_uuid.uuid, ''))[0] }} - - - {{ device_names.get(endpoint.device_id.device_uuid.uuid, endpoint.device_id.device_uuid.uuid) }} - - - - - - - {{ endpoints_data.get(endpoint.endpoint_uuid.uuid, ('', '-'))[1] }} + {{endpoint.device_id.device_uuid.uuid}} +
- {{ field_descriptor.name }} + {{ field_descriptor }} {{ field_value }}
- c_slots - - {{ c_slots }} -
@@ -133,26 +112,9 @@ {%endif%}
+ +{% endfor %} - + {% endblock %} diff --git a/src/webui/service/templates/opticalconfig/home.html b/src/webui/service/templates/opticalconfig/home.html index b212785905c76d62e88addbb03614d5756717d5d..3bdb5dd65108d6240672bbdcaf55ff50fe92346c 100644 --- a/src/webui/service/templates/opticalconfig/home.html +++ b/src/webui/service/templates/opticalconfig/home.html @@ -41,6 +41,13 @@ Refresh
+
+ + +
@@ -109,5 +116,26 @@
--> + + {% endblock %} diff --git a/src/webui/service/templates/opticalconfig/lightpaths.html b/src/webui/service/templates/opticalconfig/lightpaths.html new file mode 100644 index 0000000000000000000000000000000000000000..dba83ca4361b64b212eafb8a8d9c887bec3de42a --- /dev/null +++ b/src/webui/service/templates/opticalconfig/lightpaths.html @@ -0,0 +1,73 @@ + + +{% extends 'base.html' %} + +{% block content %} +
+
+ +
+ +
+ +

Light Paths

+{% for field_descriptor, field_value in light_paths.items() %} + + +
+
+
+ +
+ +Name : {{field_descriptor}} + + + + + + + + + + {% for k, v in field_value.items() %} + + + + + + + + {% endfor %} + + + +
KeyValue
+ {{ k }} + + {{ v }} +
+ +{% endfor %} + + + + +{% endblock %} diff --git a/src/webui/service/templates/opticalconfig/opticalbands.html b/src/webui/service/templates/opticalconfig/opticalbands.html new file mode 100644 index 0000000000000000000000000000000000000000..1c5259ca343ce0ae53825facf2a12da482574e97 --- /dev/null +++ b/src/webui/service/templates/opticalconfig/opticalbands.html @@ -0,0 +1,89 @@ + + +{% extends 'base.html' %} + +{% block content %} +
+
+ +
+ +
+ +

Optical Bands

+{% for field_descriptor, field_value in optical_bands.items() %} + + +
+
+
+ +
+ + +Name : {{field_descriptor}} + + + + + + + + + + {% for k, v in field_value.items() %} + + + {% if k == 'service_uuid' %} + + + + + + {%else %} + + + + + + {%endif%} + {% endfor %} + + +
KeyValue
Related Service + {{v}} + + + + + + +
+ {{ k }} + + {{ v }} +
+ +{% endfor %} + + + +{% endblock %} + diff --git a/src/webui/service/templates/opticalconfig/opticallinks.html b/src/webui/service/templates/opticalconfig/opticallinks.html new file mode 100644 index 0000000000000000000000000000000000000000..484784d48befb25f49f525864d808ff0eabd7e7d --- /dev/null +++ b/src/webui/service/templates/opticalconfig/opticallinks.html @@ -0,0 +1,120 @@ + + +{% extends 'base.html' %} + +{% block content %} +
+
+ +
+ +
+{% for link in optical_links %} +

Link : {{ link.name }}

+ + +
+
+
+ UUID: {{ link.link_id.link_uuid.uuid }}
+ +
+
+ + + + + + + + + + + {% for endpoint in link.link_endpoint_ids %} + + + + + + + {% endfor %} + +
Endpoint UUIDDevice UUID
+ {{ endpoint.endpoint_uuid.uuid }} + + {{endpoint.device_id.device_uuid.uuid}} + +
+
+
+ + + +Optical Link Detail: + + + + + + + + + {% for field_descriptor, field_value in link.optical_details.items() %} + + + + + + + + {% endfor %} + + {%if l_slots %} + + + + + {%endif%} + {%if s_slots %} + + + + + {%endif%} + +
KeyValue
+ {{ field_descriptor }} + + {{ field_value }} +
+ l_slots + + {{ l_slots }} +
+ s_slots + + {{ s_slots }} +
+ +{% endfor %} + + + +{% endblock %} diff --git a/src/ztp/target/generated-sources/grpc/context/ContextOuterClass.java b/src/ztp/target/generated-sources/grpc/context/ContextOuterClass.java index 956bf8539794f65ec4baa46d7f087a009881544c..b601360e0358d93920ac50789b7cbab38d64e9b9 100644 --- a/src/ztp/target/generated-sources/grpc/context/ContextOuterClass.java +++ b/src/ztp/target/generated-sources/grpc/context/ContextOuterClass.java @@ -773,6 +773,10 @@ public final class ContextOuterClass { * SERVICETYPE_ACL = 10; */ SERVICETYPE_ACL(10), + /** + * SERVICETYPE_IP_LINK = 11; + */ + SERVICETYPE_IP_LINK(11), UNRECOGNIZED(-1); /** @@ -830,6 +834,11 @@ public final class ContextOuterClass { */ public static final int SERVICETYPE_ACL_VALUE = 10; + /** + * SERVICETYPE_IP_LINK = 11; + */ + public static final int SERVICETYPE_IP_LINK_VALUE = 11; + public final int getNumber() { if (this == UNRECOGNIZED) { throw new java.lang.IllegalArgumentException("Can't get the number of an unknown enum value."); @@ -875,6 +884,8 @@ public final class ContextOuterClass { return SERVICETYPE_INT; case 10: return SERVICETYPE_ACL; + case 11: + return SERVICETYPE_IP_LINK; default: return null; }