diff --git a/deploy/crdb.sh b/deploy/crdb.sh index 42b49fe984d08c8fb2cae14e68f0a6d2a7a726dd..e180b2722aa3072bafdd1c7df9ed7b738adf5901 100755 --- a/deploy/crdb.sh +++ b/deploy/crdb.sh @@ -387,6 +387,7 @@ if [ "$CRDB_DEPLOY_MODE" == "single" ]; then fi crdb_deploy_single + sleep 3 if [ "$CRDB_DROP_DATABASE_IF_EXISTS" == "YES" ]; then crdb_drop_databases_single @@ -397,6 +398,7 @@ elif [ "$CRDB_DEPLOY_MODE" == "cluster" ]; then fi crdb_deploy_cluster + sleep 3 if [ "$CRDB_DROP_DATABASE_IF_EXISTS" == "YES" ]; then crdb_drop_databases_cluster diff --git a/proto/context.proto b/proto/context.proto index bb769a5a2cc37c7137be1da5f4e47edf41628f5e..f7659a01e101e0f61168cb0503133bf75bc892a9 100644 --- a/proto/context.proto +++ b/proto/context.proto @@ -565,6 +565,9 @@ message Location { oneof location { string region = 1; GPS_Position gps_position = 2; + + string interface=3; + string circuit_pack=4; } } diff --git a/src/common/DeviceTypes.py b/src/common/DeviceTypes.py index 23ebe19d681bd0ba774c8f3f4435c233369d0e28..6f003042c953ae52827eef8c05fde5b7e71d570a 100644 --- a/src/common/DeviceTypes.py +++ b/src/common/DeviceTypes.py @@ -48,6 +48,7 @@ class DeviceTypeEnum(Enum): PACKET_SWITCH = 'packet-switch' XR_CONSTELLATION = 'xr-constellation' QKD_NODE = 'qkd-node' + OPEN_ROADM = 'openroadm' # ETSI TeraFlowSDN controller TERAFLOWSDN_CONTROLLER = 'teraflowsdn' diff --git a/src/common/tests/MockServicerImpl_Context.py b/src/common/tests/MockServicerImpl_Context.py index 03098dae138793958dc2fa2c4ddaebd7ff03c2e5..3d6e038af266cda46a68621dd4c9b135d3916a4d 100644 --- a/src/common/tests/MockServicerImpl_Context.py +++ b/src/common/tests/MockServicerImpl_Context.py @@ -21,9 +21,11 @@ from common.proto.context_pb2 import ( Device, DeviceEvent, DeviceFilter, DeviceId, DeviceIdList, DeviceList, Empty, EventTypeEnum, Link, LinkEvent, LinkId, LinkIdList, LinkList, + OpticalLink, OpticalLinkList, Service, ServiceEvent, ServiceFilter, ServiceId, ServiceIdList, ServiceList, Slice, SliceEvent, SliceFilter, SliceId, SliceIdList, SliceList, - Topology, TopologyDetails, TopologyEvent, TopologyId, TopologyIdList, TopologyList) + Topology, TopologyDetails, TopologyEvent, TopologyId, TopologyIdList, TopologyList +) from common.proto.context_pb2_grpc import ContextServiceServicer from common.proto.policy_pb2 import PolicyRule, PolicyRuleId, PolicyRuleIdList, PolicyRuleList from common.tools.grpc.Tools import grpc_message_to_json, grpc_message_to_json_string @@ -31,8 +33,10 @@ from common.tools.object_factory.Device import json_device_id from common.tools.object_factory.Link import json_link_id from .InMemoryObjectDatabase import InMemoryObjectDatabase from .MockMessageBroker import ( - TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_SLICE, TOPIC_TOPOLOGY, TOPIC_POLICY, - MockMessageBroker, notify_event) + TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK, + TOPIC_SERVICE, TOPIC_SLICE, TOPIC_TOPOLOGY, TOPIC_POLICY, + MockMessageBroker, notify_event +) LOGGER = logging.getLogger(__name__) @@ -579,6 +583,7 @@ class MockServicerImpl_Context(ContextServiceServicer): LOGGER.debug('[SelectService] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply + # ----- Connection ------------------------------------------------------------------------------------------------- def ListConnectionIds(self, request : ServiceId, context : grpc.ServicerContext) -> ConnectionIdList: @@ -628,6 +633,9 @@ class MockServicerImpl_Context(ContextServiceServicer): LOGGER.debug('[GetConnectionEvents] request={:s}'.format(grpc_message_to_json_string(request))) for message in self.msg_broker.consume({TOPIC_CONNECTION}): yield ConnectionEvent(**json.loads(message.content)) + + # ----- Policy Rule ------------------------------------------------------------------------------------------------ + def ListPolicyRuleIds(self, request : Empty, context : grpc.ServicerContext): # pylint: disable=unused-argument LOGGER.debug('[ListPolicyRuleIds] request={:s}'.format(grpc_message_to_json_string(request))) reply = PolicyRuleIdList(policyRuleIdList=[ @@ -666,3 +674,71 @@ class MockServicerImpl_Context(ContextServiceServicer): reply = self._del(request, 'policy', policy_uuid, rule_id_field, TOPIC_CONTEXT, context) LOGGER.debug('[RemovePolicyRule] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply + + + # ----- Optical Link ----------------------------------------------------------------------------------------------- + + def GetOpticalLinkList(self, request : Empty, context : grpc.ServicerContext) -> OpticalLinkList: + LOGGER.debug('[GetOpticalLinkList] request={:s}'.format(grpc_message_to_json_string(request))) + reply = OpticalLinkList(optical_links=self.obj_db.get_entries('optical_link')) + LOGGER.debug('[GetOpticalLinkList] reply={:s}'.format(grpc_message_to_json_string(reply))) + return reply + + def GetOpticalLink(self, request : LinkId, context : grpc.ServicerContext) -> OpticalLink: + LOGGER.debug('[GetOpticalLink] request={:s}'.format(grpc_message_to_json_string(request))) + reply = self.obj_db.get_entry('optical_link', request.link_uuid.uuid, context) + LOGGER.debug('[GetOpticalLink] reply={:s}'.format(grpc_message_to_json_string(reply))) + return reply + + def SetOpticalLink(self, request : OpticalLink, context : grpc.ServicerContext) -> Empty: + LOGGER.debug('[SetOpticalLink] request={:s}'.format(grpc_message_to_json_string(request))) + link_uuid = request.link_id.link_uuid.uuid + reply, link = self._set(request, 'optical_link', link_uuid, 'link_id', TOPIC_LINK) + + context_topology_uuids : Set[Tuple[str, str]] = set() + context_topology_uuids.add((DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME)) + for endpoint_id in link.link_endpoint_ids: + endpoint_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid + if len(endpoint_context_uuid) == 0: endpoint_context_uuid = DEFAULT_CONTEXT_NAME + endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid + if len(endpoint_topology_uuid) == 0: endpoint_topology_uuid = DEFAULT_TOPOLOGY_NAME + context_topology_uuids.add((endpoint_context_uuid, endpoint_topology_uuid)) + + for context_uuid,topology_uuid in context_topology_uuids: + container_name = 'topology[{:s}]'.format(str(context_uuid)) + topology = self.obj_db.get_entry(container_name, topology_uuid, context) + for _optical_link_id in topology.optical_link_ids: + if _optical_link_id.link_uuid.uuid == link_uuid: break + else: + # link not found, add it + topology.optical_link_ids.add().link_uuid.uuid = link_uuid + + reply = Empty() + LOGGER.debug('[SetOpticalLink] reply={:s}'.format(grpc_message_to_json_string(reply))) + return reply + + def DeleteOpticalLink(self, request : LinkId, context : grpc.ServicerContext) -> Empty: + LOGGER.debug('[DeleteOpticalLink] request={:s}'.format(grpc_message_to_json_string(request))) + link_uuid = request.link_uuid.uuid + optical_link = self.obj_db.get_entry('optical_link', link_uuid, context) + reply = self._del(request, 'optical_link', link_uuid, 'link_id', TOPIC_LINK, context) + + context_topology_uuids : Set[Tuple[str, str]] = set() + context_topology_uuids.add((DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME)) + for endpoint_id in optical_link.link_endpoint_ids: + endpoint_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid + if len(endpoint_context_uuid) == 0: endpoint_context_uuid = DEFAULT_CONTEXT_NAME + endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid + if len(endpoint_topology_uuid) == 0: endpoint_topology_uuid = DEFAULT_TOPOLOGY_NAME + context_topology_uuids.add((endpoint_context_uuid, endpoint_topology_uuid)) + + for context_uuid,topology_uuid in context_topology_uuids: + container_name = 'topology[{:s}]'.format(str(context_uuid)) + topology = self.obj_db.get_entry(container_name, topology_uuid, context) + for optical_link_id in topology.optical_link_ids: + if optical_link_id.link_uuid.uuid == link_uuid: + topology.optical_link_ids.remove(optical_link_id) + break + + LOGGER.debug('[DeleteOpticalLink] reply={:s}'.format(grpc_message_to_json_string(reply))) + return reply diff --git a/src/common/tools/descriptor/Loader.py b/src/common/tools/descriptor/Loader.py index 958536f26c3c63468f1af4a6d14130b6e12fd08f..02c3e38d37a8dfa21d2a608ea846554746ba6faf 100644 --- a/src/common/tools/descriptor/Loader.py +++ b/src/common/tools/descriptor/Loader.py @@ -133,11 +133,11 @@ class DescriptorLoader: self.__topologies = self.__descriptors.get('topologies' , []) self.__devices = self.__descriptors.get('devices' , []) self.__links = self.__descriptors.get('links' , []) + self.__optical_links = self.__descriptors.get('optical_links',[]) self.__services = self.__descriptors.get('services' , []) self.__slices = self.__descriptors.get('slices' , []) self.__ietf_slices = self.__descriptors.get('ietf-network-slice-service:network-slice-services', {}) self.__connections = self.__descriptors.get('connections', []) - self.__optical_links = self.__descriptors.get('optical_links',[]) if len(self.__ietf_slices) > 0: for slice_service in self.__ietf_slices["slice-service"]: @@ -249,6 +249,12 @@ class DescriptorLoader: @property def num_links(self) -> int: return len(self.__links) + @property + def optical_links(self) -> List[Dict]: return self.__optical_links + + @property + def num_optical_links(self) -> int: return len(self.__optical_links) + @property def services(self) -> Dict[str, List[Dict]]: _services = {} @@ -286,9 +292,6 @@ class DescriptorLoader: @property def num_connections(self) -> int: return len(self.__connections) - - @property - def optical_links(self) -> List[Dict]: return self.__optical_links def process(self) -> TypeResults: # Format CustomConfigRules in Devices, Services and Slices provided in JSON format @@ -314,14 +317,15 @@ class DescriptorLoader: controllers, network_devices = split_controllers_and_network_devices(self.__devices) self.__ctx_cli.connect() - self._process_descr('context', 'add', self.__ctx_cli.SetContext, Context, self.__contexts_add ) - self._process_descr('topology', 'add', self.__ctx_cli.SetTopology, Topology, self.__topologies_add) - self._process_descr('controller', 'add', self.__ctx_cli.SetDevice, Device, controllers ) - self._process_descr('device', 'add', self.__ctx_cli.SetDevice, Device, network_devices ) - self._process_descr('link', 'add', self.__ctx_cli.SetLink, Link, self.__links ) - self._process_descr('service', 'add', self.__ctx_cli.SetService, Service, self.__services ) - self._process_descr('slice', 'add', self.__ctx_cli.SetSlice, Slice, self.__slices ) - self._process_descr('connection', 'add', self.__ctx_cli.SetConnection, Connection, self.__connections ) + self._process_descr('context', 'add', self.__ctx_cli.SetContext, Context, self.__contexts_add ) + self._process_descr('topology', 'add', self.__ctx_cli.SetTopology, Topology, self.__topologies_add) + self._process_descr('controller', 'add', self.__ctx_cli.SetDevice, Device, controllers ) + self._process_descr('device', 'add', self.__ctx_cli.SetDevice, Device, network_devices ) + self._process_descr('link', 'add', self.__ctx_cli.SetLink, Link, self.__links ) + self._process_descr('link', 'add', self.__ctx_cli.SetOpticalLink, OpticalLink, self.__optical_links ) + self._process_descr('service', 'add', self.__ctx_cli.SetService, Service, self.__services ) + self._process_descr('slice', 'add', self.__ctx_cli.SetSlice, Slice, self.__slices ) + self._process_descr('connection', 'add', self.__ctx_cli.SetConnection, Connection, self.__connections ) # By default the Context component automatically assigns devices and links to topologies based on their # endpoints, and assigns topologies, services, and slices to contexts based on their identifiers. @@ -358,11 +362,11 @@ class DescriptorLoader: self._process_descr('device', 'add', self.__dev_cli.AddDevice, Device, network_devices_add ) self._process_descr('device', 'config', self.__dev_cli.ConfigureDevice, Device, self.__devices_config) self._process_descr('link', 'add', self.__ctx_cli.SetLink, Link, self.__links ) + self._process_descr('link', 'add', self.__ctx_cli.SetOpticalLink, OpticalLink, self.__optical_links ) self._process_descr('service', 'add', self.__svc_cli.CreateService, Service, self.__services_add ) self._process_descr('service', 'update', self.__svc_cli.UpdateService, Service, self.__services ) self._process_descr('slice', 'add', self.__slc_cli.CreateSlice, Slice, self.__slices_add ) self._process_descr('slice', 'update', self.__slc_cli.UpdateSlice, Slice, self.__slices ) - self._process_descr('link', 'add', self.__ctx_cli.SetOpticalLink, OpticalLink, self.__optical_links ) # By default the Context component automatically assigns devices and links to topologies based on their # endpoints, and assigns topologies, services, and slices to contexts based on their identifiers. @@ -420,6 +424,9 @@ class DescriptorLoader: response = self.__ctx_cli.ListLinks(Empty()) assert len(response.links) == self.num_links + response = self.__ctx_cli.GetOpticalLinkList(Empty()) + assert len(response.optical_links) == self.num_optical_links + for context_uuid, num_services in self.num_services.items(): response = self.__ctx_cli.ListServices(ContextId(**json_context_id(context_uuid))) assert len(response.services) == num_services @@ -440,6 +447,9 @@ class DescriptorLoader: for service in service_list: self.__ctx_cli.RemoveService(ServiceId(**service['service_id'])) + for optical_link in self.optical_links: + self.__ctx_cli.DeleteOpticalLink(LinkId(**optical_link['link_id'])) + for link in self.links: self.__ctx_cli.RemoveLink(LinkId(**link['link_id'])) @@ -470,6 +480,9 @@ class DescriptorLoader: for service in service_list: self.__svc_cli.DeleteService(ServiceId(**service['service_id'])) + for optical_link in self.optical_links: + self.__ctx_cli.DeleteOpticalLink(LinkId(**optical_link['link_id'])) + for link in self.links: self.__ctx_cli.RemoveLink(LinkId(**link['link_id'])) @@ -524,3 +537,6 @@ def validate_empty_scenario(context_client : ContextClient) -> None: response = context_client.ListLinks(Empty()) assert len(response.links) == 0 + + response = context_client.GetOpticalLinkList(Empty()) + assert len(response.optical_links) == 0 diff --git a/src/context/service/database/OpticalConfig.py b/src/context/service/database/OpticalConfig.py index ad38751eef9c73b734c13feeeabbc3329d58df0d..ea4c456c1545f3749557512390f4de14419c0787 100644 --- a/src/context/service/database/OpticalConfig.py +++ b/src/context/service/database/OpticalConfig.py @@ -22,9 +22,10 @@ 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.RoadmModel import RoadmTypeModel, ChannelModel -from .uuids.OpticalConfig import ( - channel_get_uuid, opticalconfig_get_uuid, transponder_get_uuid, roadm_get_uuid +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 ) from .Events import notify_event_opticalconfig @@ -50,12 +51,13 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig): device_id = request.device_id device_uuid = request.device_id.device_uuid.uuid channels = [] + interfaces = [] transponder = [] roadms = [] channel_namespace = None OpticalConfig_data = [] - config_type=None - #is_transpondre=False + config_type = None + #is_transpondre = False opticalconfig_uuid = opticalconfig_get_uuid(device_id) if request.config: @@ -139,22 +141,46 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig): "opticalconfig_uuid" : opticalconfig_uuid, }) + if config_type == DeviceTypeEnum.OPEN_ROADM._value_: + if 'interfaces' in config: + for interface in config['interfaces']: + interfaces.append({ + "interface_uuid" : interface_get_uuid(interface['name']), + 'name' : interface["name"], + "type" : interface["type"], + "administrative_state": interface["administrative_state"], + "circuit_pack_name" : interface["circuit_pack_name"], + "port" : interface["port"], + "interface_list" : interface["interface_list"], + "frequency" : interface["frequency"], + "width" : interface["width"], + "roadm_uuid" : roadm_get_uuid(device_id), + }) + roadms.append({ + "roadm_uuid" : roadm_get_uuid(device_id), + "opticalconfig_uuid": opticalconfig_uuid, + }) + LOGGER.info(f"open_roadm") + OpticalConfig_data.append({ "opticalconfig_uuid" : opticalconfig_uuid, # "transcievers" : transceivers, # "interfaces" :"", "channel_namespace" : channel_namespace , "endpoints" : [json.dumps(endpoint) for endpoint in config.get("endpoints",[])], - "device_uuid": device_uuid, - "type":config_type + "device_uuid" : device_uuid, + "type" : config_type }) + LOGGER.info(f"added OpticalConfig_data {OpticalConfig_data}") + LOGGER.info(f"added interfaces {interfaces}") + def callback(session:Session)->bool: stmt = insert(OpticalConfigModel).values(OpticalConfig_data) stmt = stmt.on_conflict_do_update( index_elements=[OpticalConfigModel.opticalconfig_uuid], set_=dict( - channel_namespace=stmt.excluded.channel_namespace + channel_namespace = stmt.excluded.channel_namespace ) ) stmt = stmt.returning(OpticalConfigModel.opticalconfig_uuid) @@ -163,16 +189,15 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig): if (len(transponder)>0): stmt = insert(TransponderTypeModel).values(transponder) stmt = stmt.on_conflict_do_update( - index_elements=[TransponderTypeModel.transponder_uuid], - set_=dict( - transcievers= stmt.excluded.transcievers , - ) - + index_elements=[TransponderTypeModel.transponder_uuid], + set_=dict( + transcievers = stmt.excluded.transcievers, ) + ) stmt = stmt.returning(TransponderTypeModel.transponder_uuid) transponder_id = session.execute(stmt).fetchone() - if (len(channels) > 0): + if len(channels) > 0: stmt = insert(OpticalChannelModel).values(channels) stmt = stmt.on_conflict_do_update( index_elements=[OpticalChannelModel.channel_uuid], @@ -192,18 +217,18 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig): stmt = stmt.on_conflict_do_update( index_elements=[RoadmTypeModel.roadm_uuid], set_=dict( - circuits=stmt.excluded.circuits + opticalconfig_uuid = stmt.excluded.opticalconfig_uuid ) ) stmt = stmt.returning(RoadmTypeModel.roadm_uuid) roadm_id = session.execute(stmt).fetchone() - if (channels is not None and len(channels) > 0): + if len(channels) > 0: stmt = insert(ChannelModel).values(channels) stmt = stmt.on_conflict_do_update( - index_elements=[ChannelModel.channel_uuid ], + index_elements=[ChannelModel.channel_uuid], set_=dict( - band_name = stmt.excluded.band_name , + band_name = stmt.excluded.band_name, lower_frequency = stmt.excluded.lower_frequency, upper_frequency = stmt.excluded.upper_frequency, type = stmt.excluded.type, @@ -211,11 +236,41 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig): dest_port = stmt.excluded.dest_port, src_port = stmt.excluded.src_port, channel_index = stmt.excluded.channel_index, - optical_band_parent = stmt.excluded.optical_band_parent + optical_band_parent = stmt.excluded.optical_band_parent, ) ) stmt = stmt.returning(ChannelModel.channel_uuid) opticalChannel_id = session.execute(stmt).fetchone() + + if config_type == DeviceTypeEnum.OPEN_ROADM._value_: + if len(roadms) > 0: + stmt = insert(RoadmTypeModel).values(roadms) + stmt = stmt.on_conflict_do_update( + index_elements=[RoadmTypeModel.roadm_uuid], + set_=dict( + opticalconfig_uuid = stmt.excluded.opticalconfig_uuid + ) + ) + stmt = stmt.returning(RoadmTypeModel.roadm_uuid) + roadm_id = session.execute(stmt).fetchone() + + if len(interfaces) > 0: + stmt = insert(ORInterfaceModel).values(interfaces) + stmt = stmt.on_conflict_do_update( + index_elements=[ORInterfaceModel.interface_uuid], + set_=dict( + name = stmt.excluded.name, + frequency = stmt.excluded.frequency, + administrative_state = stmt.excluded.administrative_state, + type = stmt.excluded.type, + circuit_pack_name = stmt.excluded.circuit_pack_name, + port = stmt.excluded.port, + interface_list = stmt.excluded.interface_list, + width = stmt.excluded.width, + ) + ) + stmt = stmt.returning(ORInterfaceModel.interface_uuid) + opticalChannel_id = session.execute(stmt).fetchone() opticalconfig_id = run_transaction(sessionmaker(bind=db_engine), callback) return {'opticalconfig_uuid': opticalconfig_id} @@ -223,15 +278,15 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig): def update_opticalconfig(db_engine : Engine, request : OpticalConfig): opticalconfig_id = OpticalConfigId() device_id = request.device_id - device_uuid = request.device_id.device_uuid.uuid + device_uuid = request.device_id.device_uuid.uuid channels = [] - transponder=[] - roadms=[] - channel_namespace= None + transponder = [] + roadms = [] + channel_namespace = None OpticalConfig_data = [] - config_type=None - #is_transpondre=False - opticalconfig_uuid =opticalconfig_get_uuid(device_id) + config_type = None + #is_transpondre = False + opticalconfig_uuid = opticalconfig_get_uuid(device_id) if request.config : config = json.loads(request.config) @@ -378,7 +433,7 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): stmt = stmt.on_conflict_do_update( index_elements=[TransponderTypeModel.transponder_uuid], set_=dict( - transcievers= stmt.excluded.transcievers, + transcievers = stmt.excluded.transcievers, ) ) stmt = stmt.returning(TransponderTypeModel.transponder_uuid) @@ -387,13 +442,13 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): if len(channels) > 0: stmt = insert(OpticalChannelModel).values(channels) stmt = stmt.on_conflict_do_update( - index_elements=[OpticalChannelModel.channel_uuid ], + 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) @@ -404,7 +459,7 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): stmt = stmt.on_conflict_do_update( index_elements=[RoadmTypeModel.roadm_uuid], set_=dict( - circuits=stmt.excluded.circuits + circuits = stmt.excluded.circuits ) ) stmt = stmt.returning(RoadmTypeModel.roadm_uuid) @@ -415,13 +470,13 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): stmt = stmt.on_conflict_do_update( index_elements=[ChannelModel.channel_uuid ], set_=dict( - band_name= stmt.excluded.band_name , + band_name = stmt.excluded.band_name, lower_frequency = stmt.excluded.lower_frequency, upper_frequency = stmt.excluded.upper_frequency, - type=stmt.excluded.type, - status=stmt.excluded.status, - dest_port=stmt.excluded.dest_port, - src_port=stmt.excluded.src_port, + type = stmt.excluded.type, + status = stmt.excluded.status, + dest_port = stmt.excluded.dest_port, + src_port = stmt.excluded.src_port, ) ) stmt = stmt.returning(ChannelModel.channel_uuid) diff --git a/src/context/service/database/models/OpticalConfig/OpticalConfigModel.py b/src/context/service/database/models/OpticalConfig/OpticalConfigModel.py index fa77898337055c88edc120aa53e517ffcd6ed2b1..f9f6655e39ed84468af4b20f3f8e7d2da8855537 100644 --- a/src/context/service/database/models/OpticalConfig/OpticalConfigModel.py +++ b/src/context/service/database/models/OpticalConfig/OpticalConfigModel.py @@ -20,10 +20,10 @@ from context.service.database.models._Base import _Base class OpticalConfigModel(_Base): __tablename__ = 'optical_config' - opticalconfig_uuid = Column(String, primary_key=True) - channel_namespace = Column(String, nullable=True) - endpoints = Column(ARRAY(String), nullable=True) - type = Column(String,nullable=False) + opticalconfig_uuid = Column(String, primary_key = True) + channel_namespace = Column(String, nullable = True) + endpoints = Column(ARRAY(String), nullable = True) + type = Column(String, nullable = False) # transcievers = Column(ARRAY(String), nullable=True) # interfaces = Column(String, nullable=True) @@ -51,14 +51,20 @@ class OpticalConfigModel(_Base): "type" : self.type } if self.type =="optical-transponder" : - channels= [transponer.dump() for transponer in self.transponders ][0] - obj['channels']=channels['channels'] if 'channels' in channels else None - obj['transceivers']=channels['transceivers'] if 'transceivers' in channels else None - obj['interfaces']=channels['interfaces'] if 'interfaces' in channels else None - obj['trasponder_uuid']=channels['trasponder_uuid'] if 'trasponder_uuid' in channels else None + channels = [transponer.dump() for transponer in self.transponders][0] + obj['channels' ] = channels.get('channels', None) + obj['transceivers' ] = channels.get('transceivers', None) + obj['interfaces' ] = channels.get('interfaces', None) + obj['trasponder_uuid'] = channels.get('trasponder_uuid', None) if self.type =="optical-roadm" : - channels=[roadms.dump() for roadms in self.roadms ][0] - obj['channels']=channels['channels'] if 'channels' in channels else None - obj['roadm_uuid']=channels['roadm_uuid'] if 'roadm_uuid' in channels else None + dev = [roadms.dump() for roadms in self.roadms][0] + obj['channels' ] = dev.get('channels', None) + obj['roadm_uuid'] = dev.get('roadm_uuid', None) + + if self.type =="openroadm" : + dev = [roadms.dump() for roadms in self.roadms][0] + obj['interfaces'] = dev.get('interfaces', None) + obj['roadm_uuid'] = dev.get('roadm_uuid', None) + return obj diff --git a/src/context/service/database/models/OpticalConfig/RoadmModel.py b/src/context/service/database/models/OpticalConfig/RoadmModel.py index 31ebf4cfabb2b91252b3c80884d47b93d07db033..48a921b47600bc5e06ac80d4ad642fdf2c3915a8 100644 --- a/src/context/service/database/models/OpticalConfig/RoadmModel.py +++ b/src/context/service/database/models/OpticalConfig/RoadmModel.py @@ -12,17 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -from sqlalchemy import Column, String, Integer , ForeignKey +import logging +from sqlalchemy import Column, String, Integer, ForeignKey, Float from sqlalchemy.orm import relationship from context.service.database.models._Base import _Base -class RoadmTypeModel (_Base): +class RoadmTypeModel(_Base): __tablename__ = 'roadm_type' roadm_uuid = Column(String, primary_key=True) circuits = Column(String, nullable=True) opticalconfig_uuid = Column(ForeignKey('optical_config.opticalconfig_uuid', ondelete='CASCADE'), index=True, nullable=False) channels = relationship("ChannelModel") opticalconfig = relationship('OpticalConfigModel', back_populates='roadms') + interfaces = relationship("ORInterfaceModel") def dump_id (self): return { @@ -33,6 +35,7 @@ class RoadmTypeModel (_Base): return { "channels" : [channel.dump() for channel in self.channels], "roadm_uuid" : self.dump_id(), + "interfaces" : [interface.dump() for interface in self.interfaces] } class ChannelModel(_Base): @@ -49,8 +52,7 @@ class ChannelModel(_Base): type = Column(String, nullable=False) optical_band_parent = Column(String, nullable=True) roadm_uuid = Column(ForeignKey('roadm_type.roadm_uuid', ondelete='CASCADE'), nullable=False) - - roadm = relationship('RoadmTypeModel',back_populates='channels') + roadm = relationship('RoadmTypeModel',back_populates='channels') # opticalconfig_uuid = Column(ForeignKey('optical_config.opticalconfig_uuid', ondelete='CASCADE'), primary_key=True) # opticalconfig = relationship('OpticalConfigModel', back_populates='channels') @@ -72,3 +74,35 @@ class ChannelModel(_Base): "optical_band_parent" : self.optical_band_parent, "channel_index" : self.channel_index, } + +class ORInterfaceModel (_Base): + __tablename__ = 'open_roadm_interface' + + interface_uuid = Column(String, primary_key = True) + name = Column(String, nullable = False, unique = True) + type = Column(String, nullable = True) + administrative_state = Column(String, nullable = True) + circuit_pack_name = Column(String, nullable = True) + port = Column(String, nullable = True) + interface_list = Column(String, nullable = True) + frequency = Column(Float, nullable = True) + width = Column(Integer, nullable = True) + roadm_uuid = Column(ForeignKey('roadm_type.roadm_uuid', ondelete='CASCADE'), nullable=False) + roadm = relationship('RoadmTypeModel', back_populates='interfaces') + + def dump_id (self ): + return { + "interface_uuid": self.interface_uuid + } + + def dump(self): + return { + "name" : self.name, + "type" : self.type, + "administrative_state": self.administrative_state, + "circuit_pack_name" : self.circuit_pack_name, + "port" : self.port, + "interface_list" : self.interface_list, + "frequency" : self.frequency, + "width" : self.width + } diff --git a/src/context/service/database/uuids/OpticalConfig.py b/src/context/service/database/uuids/OpticalConfig.py index f2cd443a983d5b37009ddc1c2dc821dce960ddb8..abc1bf67f7f534932316c60639d1626ca86fc06f 100644 --- a/src/context/service/database/uuids/OpticalConfig.py +++ b/src/context/service/database/uuids/OpticalConfig.py @@ -27,6 +27,17 @@ def channel_get_uuid( ('channel uuid', channel_name), ], extra_details=['Channel name is required to produce a channel UUID']) +def interface_get_uuid( + interface_name :str , allow_random : bool = False +) -> str: + if len(interface_name) > 0: + return get_uuid_from_string(interface_name) + if allow_random: return get_uuid_random() + + raise InvalidArgumentsException([ + ('interface uuid', interface_name), + ], extra_details=['interface name is required to produce a interface UUID']) + def transponder_get_uuid( opticalconfig_id : str, allow_random : bool = False ) -> str: diff --git a/src/device/service/Tools.py b/src/device/service/Tools.py index 967a6a8c19ca4da763f29bfa164f50ad88d27363..87a9b535c7d203cef927f190d83af7453f50de10 100644 --- a/src/device/service/Tools.py +++ b/src/device/service/Tools.py @@ -462,6 +462,7 @@ def update_endpoints(src_device : Device, dst_device : Device) -> None: def get_edit_target(device : Device, is_opticalband : bool) -> str: if is_opticalband: return 'optical-band' if device.device_type == DeviceTypeEnum.OPTICAL_ROADM._value_: return 'media-channel' + if device.device_type == DeviceTypeEnum.OPEN_ROADM._value_: return 'network-media-channel' return 'optical-channel' def is_key_existed(key : str, keys_dic = dict, key_name_to_use = None) -> dict: @@ -478,57 +479,78 @@ def is_key_existed(key : str, keys_dic = dict, key_name_to_use = None) -> dict: def extract_resources(config : dict, device : Device) -> list[list[dict], dict]: conditions = {} resources : list[dict] = [] - resources.append(is_key_existed('channel_namespace', config)) - resources.append(is_key_existed('add_transceiver', config)) is_opticalband = config.get('is_opticalband', False) - conditions['is_opticalband'] = is_opticalband conditions['edit_type'] = get_edit_target(device, is_opticalband) - if 'flow' in config: - #for tuple_value in config['flow'][device.name]: - source_vals = [] - dest_vals = [] - handled_flow = [] - for tuple_value in config['flow']: - source_port = None - destination_port = None - source_port_uuid, destination_port_uuid = tuple_value - if source_port_uuid != '0': - src_endpoint_obj = get_endpoint_matching(device, source_port_uuid) - source_port = src_endpoint_obj.name - source_vals.append(source_port) - if destination_port_uuid != '0': - dst_endpoint_obj = get_endpoint_matching(device, destination_port_uuid) - destination_port = dst_endpoint_obj.name - dest_vals.append(destination_port) - handled_flow.append((source_port, destination_port)) - resources.append({'resource_key': 'source_port', 'value': source_vals }) - resources.append({'resource_key': 'destination_port', 'value': dest_vals }) - resources.append({'resource_key': 'handled_flow', 'value': handled_flow}) - if 'new_config' in config: - lower_frequency = None - upper_frequency = None - resources.append(is_key_existed('target-output-power', keys_dic=config['new_config'])) - resources.append(is_key_existed('frequency', keys_dic=config['new_config'])) - 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('name', keys_dic=config['new_config'], key_name_to_use='channel_name')) - if not is_opticalband: - if 'frequency' in config['new_config'] and 'band' in config['new_config'] and conditions['edit_type'] == 'media-channel': - if config['new_config']['frequency'] is not None and config['new_config']['band'] is not None: - lower_frequency = int(int(config['new_config']['frequency']) - (int(config['new_config']['band'])/2)+1) - upper_frequency = int(int(config['new_config']['frequency']) + (int(config['new_config']['band'])/2)) - resources.append(is_key_existed('flow_id', keys_dic=config['new_config'], key_name_to_use='index')) - #resources.append({'resource_key':'index','value':config['new_config']['flow_id'] if 'flow_id' in config['new_config'] else None}) - else: - lower_frequency = config['new_config']['low-freq'] if 'low-freq' in config['new_config'] else None - upper_frequency = config['new_config']['up-freq' ] if 'up-freq' in config['new_config'] else None - resources.append(is_key_existed('ob_id', keys_dic=config['new_config'], key_name_to_use='index')) - #resources.append({'resource_key':'index','value':config['new_config']['ob_id'] if 'ob_id' in config['new_config'] else None}) - resources.append({'resource_key': 'lower-frequency', 'value': lower_frequency}) - resources.append({'resource_key': 'upper-frequency', 'value': upper_frequency}) + if device.device_type == DeviceTypeEnum.OPEN_ROADM._value_ : + ports_dic = is_key_existed('ports',keys_dic=config['new_config']) + interfaces_list = [] + config_type = is_key_existed('config_type', keys_dic=config['new_config']) + resources.append(config_type) + 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) + supporting_port = is_key_existed('supporting-port', keys_dic=port) + interfaces_list.append([ + circuit_pack_dic, + interface_list, + supporting_port + ]) + resources.append({'resource_key':'interfaces','value':interfaces_list}) + else : + resources.append(is_key_existed('channel_namespace', config)) + resources.append(is_key_existed('add_transceiver', config)) + + conditions['is_opticalband'] = is_opticalband + if 'flow' in config: + #for tuple_value in config['flow'][device.name]: + source_vals = [] + dest_vals = [] + handled_flow = [] + for tuple_value in config['flow']: + source_port = None + destination_port = None + source_port_uuid, destination_port_uuid = tuple_value + if source_port_uuid != '0': + src_endpoint_obj = get_endpoint_matching(device, source_port_uuid) + source_port = src_endpoint_obj.name + source_vals.append(source_port) + if destination_port_uuid != '0': + dst_endpoint_obj = get_endpoint_matching(device, destination_port_uuid) + destination_port = dst_endpoint_obj.name + dest_vals.append(destination_port) + handled_flow.append((source_port, destination_port)) + resources.append({'resource_key': 'source_port', 'value': source_vals }) + resources.append({'resource_key': 'destination_port', 'value': dest_vals }) + resources.append({'resource_key': 'handled_flow', 'value': handled_flow}) + if 'new_config' in config: + lower_frequency = None + upper_frequency = None + resources.append(is_key_existed('target-output-power', keys_dic=config['new_config'])) + resources.append(is_key_existed('frequency', keys_dic=config['new_config'])) + 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('name', keys_dic=config['new_config'], key_name_to_use='channel_name')) + + if not is_opticalband: + if 'frequency' in config['new_config'] and 'band' in config['new_config'] and conditions['edit_type'] == 'media-channel': + if config['new_config']['frequency'] is not None and config['new_config']['band'] is not None: + lower_frequency = int(int(config['new_config']['frequency']) - (int(config['new_config']['band'])/2)+1) + upper_frequency = int(int(config['new_config']['frequency']) + (int(config['new_config']['band'])/2)) + resources.append(is_key_existed('flow_id', keys_dic=config['new_config'], key_name_to_use='index')) + #resources.append({'resource_key':'index','value':config['new_config']['flow_id'] if 'flow_id' in config['new_config'] else None}) + else: + lower_frequency = config['new_config']['low-freq'] if 'low-freq' in config['new_config'] else None + upper_frequency = config['new_config']['up-freq' ] if 'up-freq' in config['new_config'] else None + resources.append(is_key_existed('ob_id', keys_dic=config['new_config'], key_name_to_use='index')) + #resources.append({'resource_key':'index','value':config['new_config']['ob_id'] if 'ob_id' in config['new_config'] else None}) + resources.append({'resource_key': 'lower-frequency', 'value': lower_frequency}) + resources.append({'resource_key': 'upper-frequency', 'value': upper_frequency}) return [resources, conditions] diff --git a/src/device/service/drivers/oc_driver/OCDriver.py b/src/device/service/drivers/oc_driver/OCDriver.py index 087e58b7673e752d208eaa82b30a697e4eb8d08c..7ea9f00f3926b13362bb591987daab7ffbfabdf7 100644 --- a/src/device/service/drivers/oc_driver/OCDriver.py +++ b/src/device/service/drivers/oc_driver/OCDriver.py @@ -35,8 +35,11 @@ from .templates.VPN.transponder import edit_optical_channel, change_optical_chan from .RetryDecorator import retry from context.client.ContextClient import ContextClient from common.proto.context_pb2 import OpticalConfig -from .templates.descovery_tool.transponders import transponder_values_extractor -from .templates.descovery_tool.roadms import roadm_values_extractor, openroadm_values_extractor +from .templates.discovery_tool.transponders import transponder_values_extractor +from .templates.discovery_tool.roadms import roadm_values_extractor, extract_media_channels +from .templates.discovery_tool.open_roadm import openroadm_values_extractor +from .templates.VPN.openroadm import network_media_channel_handler + DEBUG_MODE = False logging.getLogger('ncclient.manager').setLevel(logging.DEBUG if DEBUG_MODE else logging.WARNING) @@ -157,6 +160,7 @@ 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'): @@ -164,7 +168,11 @@ def edit_config( str_config_messages = edit_optical_channel(resources) elif (conditions['edit_type']=='optical-band'): #roadm optical-band - str_config_messages = create_optical_band(resources) + 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) @@ -256,19 +264,20 @@ class OCDriver(_Driver): data_xml=xml_data, resource_keys=transponder_filter_fields, dic=config ) transceivers, optical_channels_params, channel_namespace, endpoints, ports_result = extracted_values - oc_values["channels"] = optical_channels_params - oc_values["transceivers"] = transceivers + oc_values["channels" ] = optical_channels_params + oc_values["transceivers" ] = transceivers oc_values["channel_namespace"] = channel_namespace - oc_values["endpoints"] = endpoints - oc_values["ports"] = ports_result + oc_values["endpoints" ] = endpoints + oc_values["ports" ] = ports_result elif (self.__type =='openroadm'): extracted_values=openroadm_values_extractor(data_xml=xml_data, resource_keys=[], dic=oc_values) ports_result = extracted_values[1] + oc_values['interfaces'] = extracted_values[0]['interfaces'] else: extracted_values = roadm_values_extractor(data_xml=xml_data, resource_keys=[], dic=config) ports_result = extracted_values[0] - oc_values['optical_bands']=extracted_values[1] - oc_values['media_channels']=extracted_values[2] + oc_values['optical_bands' ] = extracted_values[1] + oc_values['media_channels'] = extracted_values[2] #results.append((resource_key, e)) # if validation fails, store the exception diff --git a/src/device/service/drivers/oc_driver/templates/VPN/openroadm.py b/src/device/service/drivers/oc_driver/templates/VPN/openroadm.py new file mode 100644 index 0000000000000000000000000000000000000000..f960c3d63be84f304b506d73a760441c8c6de396 --- /dev/null +++ b/src/device/service/drivers/oc_driver/templates/VPN/openroadm.py @@ -0,0 +1,187 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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 yattag import Doc, indent +import logging +from .common import seperate_port_config ,filter_config +from decimal import Decimal + + + +create_mc_err= ''' + + + + MC-TTP-DEG2-RX-193.3 + Media-Channel-TTP-193.3THz-degree-2-in + openROADM-if:mediaChannelTrailTerminationPoint + inService + DEG2-AMPRX + DEG2-AMPRX-IN + OMS-DEG2-TTP-RX + + 193.25 + 193.35 + + + + MC-TTP-DEG2-TX-193.3 + Media-Channel-TTP-193.3THz-degree-2-out + openROADM-if:mediaChannelTrailTerminationPoint + inService + DEG2-AMPTX + DEG2-AMPTX-OUT + OMS-DEG2-TTP-TX + + 193.25 + 193.35 + + + + + + + +''' + +def define_interface_name (type:str,interface_list:str,freq:int)->str: + interface_str = interface_list.split('-') + port_rank='' + port_type='' + if (len(interface_str)==4): + port_rank=interface_str[1] + port_type=interface_str[3] + elif (len(interface_str)==5): + port_rank=interface_str[2] + port_type=interface_str[3] + else : + port_rank=interface_list + port_type=interface_list+'type' + + + return f'{type.upper()}-{port_rank}-{port_type}-{freq}' + + + +def create_media_channel (resources): + + frequency_dict=next((r for r in resources if r['resource_key']== 'frequency'),None) + width_dict=next((r for r in resources if r['resource_key']== 'width'),None) + interfaces_lists =next((r for r in resources if r['resource_key']== 'interfaces'),None) + administrative_state= next((r for r in resources if r['resource_key']== 'administrative-state'),None) + min_freq= int(Decimal(frequency_dict["value"])*1000) - (int(width_dict["value"])/2) + max_freq = int(Decimal(frequency_dict["value"])*1000) + (int(width_dict["value"])/2) + #config,_,_ = filter_config(resources=resources,unwanted_keys=unwanted_keys) + + or_device_ns="http://org/openroadm/device" + or_interface_ns="http://org/openroadm/interfaces" + + results=[] + logging.info(f"from openroadm mc {resources}") + doc, tag, text = Doc().tagtext() + with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): + with tag('org-openroadm-device', ('xmlns',or_device_ns)): + + for interface in interfaces_lists["value"]: + port = next((r for r in interface if r['resource_key']=='supporting-port'),None) + circuit_pack =next((r for r in interface if r['resource_key']=='supporting-circuit-pack-name'),None) + interface_list = next((r for r in interface if r['resource_key']=='supporting-interface-list'),None) + mc_name = define_interface_name('mc-ttp',interface_list['value'],frequency_dict['value']) + interface.append({'resource_key':'mc_name','value':mc_name}) + with tag('interface'): + + with tag('name'):text(mc_name) + with tag('description'):text(f'Media-channel-{frequency_dict["value"]}THz') + with tag('type'):text("openROADM-if:mediaChannelTrailTerminationPoint") + with tag('administrative-state'):text(administrative_state["value"]) + with tag('supporting-circuit-pack-name'):text(circuit_pack["value"]) + with tag('supporting-port'):text(port["value"]) + with tag('supporting-interface-list'):text(interface_list["value"]) + with tag('mc-ttp',xmlns="http://org/openroadm/media-channel-interfaces"): + with tag('max-freq'):text(max_freq) + with tag('min-freq'):text(min_freq) + + + + result = indent( + doc.getvalue(), + indentation = ' '*2, + newline = '' + ) + results.append(result) + logging.info(f"from openroadm mc results {results}") + return [results,resources] + + + + +def create_network_media_channel (resources): + + logging.info(f"nmc resources {resources}") + + unwanted_keys= ['max-freq','min-freq'] + #config,_,_ = filter_config(resources=resources,unwanted_keys=unwanted_keys) + + or_device_ns="http://org/openroadm/device" + frequency_dict=next((r for r in resources if r['resource_key']== 'frequency'),None) + width_dict=next((r for r in resources if r['resource_key']== 'width'),None) + interfaces_lists =next((r for r in resources if r['resource_key']== 'interfaces'),None) + administrative_state= next((r for r in resources if r['resource_key']== 'administrative-state'),None) + + + results=[] + doc, tag, text = Doc().tagtext() + with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): + with tag('org-openroadm-device', ('xmlns',or_device_ns)): + for interface in interfaces_lists["value"]: + port = next((r for r in interface if r['resource_key']=='supporting-port'),None) + circuit_pack =next((r for r in interface if r['resource_key']=='supporting-circuit-pack-name'),None) + interface_list = next((r for r in interface if r['resource_key']=='mc_name'),None) + nmc_name = define_interface_name('nmc-ctp',interface_list['value'],frequency_dict['value']) + + with tag('interface'): + + with tag('name'):text(nmc_name) + with tag('description'):text(f'Media-channel-{frequency_dict["value"]}THz') + with tag('type'):text("openROADM-if:networkMediaChannelConnectionTerminationPoint") + with tag('administrative-state'):text(administrative_state["value"]) + with tag('supporting-circuit-pack-name'):text(circuit_pack["value"]) + with tag('supporting-port'):text(port["value"]) + with tag('supporting-interface-list'):text(interface_list["value"]) + with tag('nmc-ctp',xmlns="http://org/openroadm/network-media-channel-interfaces"): + with tag('frequency'):text(frequency_dict['value']) + with tag('width'):text(width_dict['value']) + + + + result = indent( + doc.getvalue(), + indentation = ' '*2, + newline = '' + ) + results.append(result) + logging.info(f"nmc message {results}") + return results + + +def network_media_channel_handler (resources): + unwanted_keys=["config_type"] + config,_,_ = filter_config(resources=resources,unwanted_keys=unwanted_keys) + mc_list,resources_updated= create_media_channel(resources=config) + nmc_list= create_network_media_channel(resources=resources_updated) + mc_list.extend(nmc_list) + + return mc_list diff --git a/src/device/service/drivers/oc_driver/templates/discovery_tool/open_roadm.py b/src/device/service/drivers/oc_driver/templates/discovery_tool/open_roadm.py new file mode 100644 index 0000000000000000000000000000000000000000..9b148c424155a01c6f9ff4df9b438b158283acd8 --- /dev/null +++ b/src/device/service/drivers/oc_driver/templates/discovery_tool/open_roadm.py @@ -0,0 +1,246 @@ + +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re,logging +import json +import lxml.etree as ET +from typing import Collection, Dict, Any +from common.proto.context_pb2 import Location +from decimal import Decimal + +def extract_roadm_circuits_pack (xml_data:str): + xml_bytes = xml_data.encode("utf-8") + root = ET.fromstring(xml_bytes) + # with open('xml.log', 'w') as f: + # print(xml_bytes, file=f) + + namespace = {'oc': "http://org/openroadm/device"} + + circuits = root.findall('.//oc:circuit-packs',namespace) + + circuits_list =[] + # print(f"component {components}") + + if (circuits is not None): + for circuit in circuits: + circuit_info ={} + circuit_ports=[] + circuit_name = circuit.find(".//oc:circuit-pack-name",namespace) + circuit_type=circuit.find(".//oc:circuit-pack-type",namespace) + circuit_adminstrative_status=circuit.find(".//oc:administrative-state",namespace) + circuit_equipment_state=circuit.find("./oc:equipment-state",namespace) + circuit_mode=circuit.find("./oc:circuit-pack-mode",namespace) + slot= circuit.find("./oc:slot",namespace) + shelf= circuit.find("./oc:shelf",namespace) + ports = circuit.findall("./oc:ports",namespace) + + + if (ports is not None): + + for port in ports : + port_info={} + port_name=port.find('./oc:port-name',namespace) + port_qual= port.find("./oc:port-qual",namespace) + + if port_name is not None : + port_info["port_name"]=port_name.text + if port_qual is not None : + port_info["port_qual"]=port_qual.text + circuit_ports.append(port_info) + # if port_info["port_qual"] == 'roadm-external': + # circuit_ports.append(port_info) + if (circuit_name is not None): + circuit_info["circuit_name"]=circuit_name.text + if (circuit_type is not None): + circuit_info["circuit_type"]=circuit_type.text + if (circuit_adminstrative_status is not None): + circuit_info["circuit_adminstrative_status"]=circuit_adminstrative_status.text + if (circuit_equipment_state is not None): + circuit_info["circuit_equipment_state"]=circuit_equipment_state.text + if (circuit_mode is not None): + circuit_info["circuit_mode"]=circuit_mode.text + if (slot is not None): + circuit_info["slot"]=slot.text + if (shelf is not None): + circuit_info["shelf"]=shelf.text + logging.info(f"circuit_ports {circuit_ports}") + circuit_info["ports"]=circuit_ports + + circuits_list.append(circuit_info) + + + + return circuits_list + + + +def extract_openroadm_info(xml_data:str): + roadm_info={"node-id":None,"node-number":None,"node-type":None,'clli':None} + xml_bytes = xml_data.encode("utf-8") + root = ET.fromstring(xml_bytes) + namespace = {'oc': "http://org/openroadm/device"} + info = root.findall('.//oc:info',namespace) + if info is not None : + for i in info : + node_id= i.find('.//oc:node-id',namespace) + node_number= i.find('.//oc:node-number',namespace) + node_type=i.find('.//oc:node-type',namespace) + clli=i.find('.//oc:clli',namespace) + if (node_id is not None): + roadm_info['node-id']=node_id.text + if (node_number is not None): + roadm_info['node-number']=node_number.text + if (node_type is not None): + roadm_info['node-type']=node_type.text + if (clli is not None): + roadm_info['clli']=clli.text + + return roadm_info + + +def extract_openroadm_interface (xml_data:str): + or_config=[] + or_interfaces=[] + + xml_bytes = xml_data.encode("utf-8") + root = ET.fromstring(xml_bytes) + # with open('xml.log', 'w') as f: + # print(xml_bytes, file=f) + + + namespace = {'oc': "http://org/openroadm/device" + , 'mc':"http://org/openroadm/media-channel-interfaces" + ,'nmc':"http://org/openroadm/network-media-channel-interfaces" + ,'or-type':'http://org/openroadm/interfaces'} + + interfaces = root.findall('.//oc:interface',namespace) + for interface in interfaces : + mc = interface.find('.//mc:mc-ttp',namespace) + name = interface.find('.//oc:name',namespace) + description = interface.find('.//oc:description',namespace) + type=interface.find('.//oc:type',namespace) + administrative_state=interface.find('.//oc:administrative-state',namespace) + circuit_pack_name=interface.find('.//oc:supporting-circuit-pack-name',namespace) + port=interface.find('.//oc:supporting-port',namespace) + interface_list =interface.find('.//oc:supporting-interface-list',namespace) + + or_interfaces.append({ + 'interface_list':name.text if name is not None else None, + 'administrative_state':administrative_state.text if administrative_state is not None else None, + 'circuit_pack_name':circuit_pack_name.text if circuit_pack_name is not None else None, + 'port':port.text if port is not None else None , + 'type':type.text if type is not None else None + }) + if mc is not None : + print (mc) + frequency=None + width=None + min_frequency = mc.find('.//mc:min-freq',namespace) + max_frequency = mc.find('.//mc:max-freq',namespace) + if min_frequency is not None and max_frequency is not None: + frequency = (Decimal(max_frequency.text) + Decimal(min_frequency.text) )/2 + width = int(( Decimal(max_frequency.text) - Decimal(min_frequency.text)) * 1000) + + + mc= { + 'name':name.text if name is not None else None, + 'description':description.text if description is not None else None , + 'type':"media_channel", + 'administrative_state':administrative_state.text if administrative_state is not None else None, + 'circuit_pack_name':circuit_pack_name.text if circuit_pack_name is not None else None, + 'port':port.text if port is not None else None , + 'interface_list': interface_list.text if interface_list is not None else None, + 'frequency': str(frequency), + 'width':width + } + or_config.append(mc) + + else : + nmc = interface.find('.//nmc:nmc-ctp',namespace) + + + if nmc is not None : + frequency = nmc.find('.//nmc:frequency',namespace) + width=nmc.find('.//nmc:width',namespace) + nmc= { + 'name':name.text if name is not None else None, + 'description':description.text if description is not None else None , + 'type':"network_media_channel", + 'administrative_state':administrative_state.text if administrative_state is not None else None, + 'circuit_pack_name':circuit_pack_name.text if circuit_pack_name is not None else None, + 'port':port.text if port is not None else None , + 'interface_list': interface_list.text if interface_list is not None else None, + 'frequency': frequency.text if frequency is not None else None, + 'width':width.text if width is not None else None + } + or_config.append(nmc) + logging.info(f"or_config for or {or_config}") + return [or_interfaces,or_config] + + +def openroadm_values_extractor (data_xml:str,resource_keys:list,dic:dict): + ports_result=[] + openroadm_info= extract_openroadm_info(data_xml) + circuits_list = extract_roadm_circuits_pack(data_xml) + interfaces,config = extract_openroadm_interface(data_xml) + dic["openroadm_info"]=openroadm_info + dic["circuits"]=circuits_list + dic['interfaces']=config + + for circuit in circuits_list : + circuit_name=circuit['circuit_name'] + location = Location() + location.circuit_pack=circuit_name + for port in circuit['ports']: + if port is not None and 'port_name' in port : + resource_key = '/endpoints/endpoint[{:s}]'.format(port["port_name"]) + resource_value = {'uuid': port["port_name"] + , 'type':port["port_qual"] if "port_qual" in port else None, + 'location':{"circuit_pack":location.circuit_pack} + } + ports_result.append((resource_key, resource_value)) + for interface in interfaces: + existed=False + circuit_name=interface['circuit_pack_name'] + interface_list=interface['interface_list'] + + location_interface=f'{interface_list}/{circuit_name}' + port = interface["port"] + type = interface['type'] + if port is not None: + for i , (key,value) in enumerate(ports_result): + if value['uuid'] == port: + new_value = value + merged_interface=None + if 'interface' in value['location']: + merged_interface= f"{value['location']['interface']},{location_interface}" + if merged_interface is not None : + new_value['location']={"interface":merged_interface} + else : + new_value['location']={"interface":location_interface} + + ports_result[i]= (key,new_value ) + existed=True + break + + if not existed: + resource_key = '/endpoints/endpoint[{:s}]'.format(port) + resource_value = {'uuid': f'{port}' + , 'type':type , + 'location':{"interface":location_interface} + } + ports_result.append((resource_key, resource_value)) + + return [dic,ports_result] diff --git a/src/device/service/drivers/oc_driver/templates/descovery_tool/roadms.py b/src/device/service/drivers/oc_driver/templates/discovery_tool/roadms.py similarity index 65% rename from src/device/service/drivers/oc_driver/templates/descovery_tool/roadms.py rename to src/device/service/drivers/oc_driver/templates/discovery_tool/roadms.py index e27fd923a4d3ec4a69b810667545d5bcae235269..f7ebfde705f9e094fc74e7c0ef8f1130345cdb30 100644 --- a/src/device/service/drivers/oc_driver/templates/descovery_tool/roadms.py +++ b/src/device/service/drivers/oc_driver/templates/discovery_tool/roadms.py @@ -161,6 +161,7 @@ def roadm_values_extractor (data_xml:str,resource_keys:list,dic:dict): namespcae= extract_channel_xmlns(data_xml,True) optical_bands=extract_optical_bands(data_xml=data_xml,namespace=namespcae) media_cahannels=extract_media_channels(data_xml) + if len(ports)>0 : for port in ports : name,type=port @@ -168,93 +169,3 @@ def roadm_values_extractor (data_xml:str,resource_keys:list,dic:dict): resource_value = {'uuid': name, 'type':type} ports_result.append((resource_key, resource_value)) return [ports_result,optical_bands,media_cahannels] - -#/////////////// OpenRoadm ////////////// - -def extract_roadm_circuits_pack (xml_data:str): - xml_bytes = xml_data.encode("utf-8") - root = ET.fromstring(xml_bytes) - # with open('xml.log', 'w') as f: - # print(xml_bytes, file=f) - - namespace = {'oc': "http://org/openroadm/device"} - circuits = root.findall('.//oc:circuit-packs',namespace) - circuits_list =[] - # print(f"component {components}") - if (circuits is not None): - for circuit in circuits: - circuit_info ={} - circuit_ports=[] - circuit_name = circuit.find(".//oc:circuit-pack-name",namespace) - circuit_type=circuit.find(".//oc:circuit-pack-type",namespace) - circuit_adminstrative_status=circuit.find(".//oc:administrative-state",namespace) - circuit_equipment_state=circuit.find("./oc:equipment-state",namespace) - circuit_mode=circuit.find("./oc:circuit-pack-mode",namespace) - slot= circuit.find("./oc:slot",namespace) - shelf= circuit.find("./oc:shelf",namespace) - ports = circuit.findall("./oc:ports",namespace) - - if (ports is not None): - for port in ports : - port_info={} - port_name=port.find('./oc:port-name',namespace) - port_qual= port.find("./oc:port-qual",namespace) - if port_name is not None : - port_info["port_name"]=port_name.text - if port_qual is not None : - port_info["port_qual"]=port_qual.text - circuit_ports.append(port_info) - if (circuit_name is not None): - circuit_info["circuit_name"]=circuit_name.text - if (circuit_type is not None): - circuit_info["circuit_type"]=circuit_type.text - if (circuit_adminstrative_status is not None): - circuit_info["circuit_adminstrative_status"]=circuit_adminstrative_status.text - if (circuit_equipment_state is not None): - circuit_info["circuit_equipment_state"]=circuit_equipment_state.text - if (circuit_mode is not None): - circuit_info["circuit_mode"]=circuit_mode.text - if (slot is not None): - circuit_info["slot"]=slot.text - if (shelf is not None): - circuit_info["shelf"]=shelf.text - circuit_info["ports"]=circuit_ports - circuits_list.append(circuit_info) - return circuits_list - -def extract_openroadm_info(xml_data:str): - roadm_info={"node-id":None,"node-number":None,"node-type":None,'clli':None} - xml_bytes = xml_data.encode("utf-8") - root = ET.fromstring(xml_bytes) - namespace = {'oc': "http://org/openroadm/device"} - info = root.findall('.//oc:info',namespace) - if info is not None : - for i in info : - node_id= i.find('.//oc:node-id',namespace) - node_number= i.find('.//oc:node-number',namespace) - node_type=i.find('.//oc:node-type',namespace) - clli=i.find('.//oc:clli',namespace) - if (node_id is not None): - roadm_info['node-id']=node_id.text - if (node_number is not None): - roadm_info['node-number']=node_number.text - if (node_type is not None): - roadm_info['node-type']=node_type.text - if (clli is not None): - roadm_info['clli']=clli.text - return roadm_info - -def openroadm_values_extractor (data_xml:str,resource_keys:list,dic:dict): - ports_result=[] - openroadm_info= extract_openroadm_info(data_xml) - circuits_list = extract_roadm_circuits_pack(data_xml) - dic["openroadm_info"]=openroadm_info - dic["circuits"]=circuits_list - - for circuit in circuits_list : - for port in circuit['ports']: - if port is not None and 'port_name' in port : - resource_key = '/endpoints/endpoint[{:s}]'.format(port["port_name"]) - resource_value = {'uuid': port["port_name"], 'type':port["port_qual"] if "port_qual" in port else None} - ports_result.append((resource_key, resource_value)) - return [dic,ports_result] diff --git a/src/device/service/drivers/oc_driver/templates/descovery_tool/transponders.py b/src/device/service/drivers/oc_driver/templates/discovery_tool/transponders.py similarity index 100% rename from src/device/service/drivers/oc_driver/templates/descovery_tool/transponders.py rename to src/device/service/drivers/oc_driver/templates/discovery_tool/transponders.py diff --git a/src/opticalcontroller/Dockerfile b/src/opticalcontroller/Dockerfile index faea3b2e056768ef9947db108df61928c8a177cb..25805627587f957c341f4e113d0388bf11c73efa 100644 --- a/src/opticalcontroller/Dockerfile +++ b/src/opticalcontroller/Dockerfile @@ -67,5 +67,4 @@ COPY src/context/client/. context/client/ COPY src/opticalcontroller/. opticalcontroller/ # Start the service -WORKDIR /var/teraflow/opticalcontroller -ENTRYPOINT ["python", "OpticalController.py"] +ENTRYPOINT ["python", "-m", "opticalcontroller.OpticalController"] diff --git a/src/opticalcontroller/OpticalController.py b/src/opticalcontroller/OpticalController.py index 97e097c8a94382b71903b4a57ab66382beec5eb2..c0bc877e9286e03938024c5a6195c4a9d6b04711 100644 --- a/src/opticalcontroller/OpticalController.py +++ b/src/opticalcontroller/OpticalController.py @@ -12,16 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -import time +import logging, time from flask import Flask from flask import render_template from flask_restplus import Resource, Api -from tools import * -from variables import * -from RSA import RSA -from common.proto.context_pb2 import TopologyId 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__) global rsa global links_dict @@ -113,29 +115,26 @@ class AddFlexLightpath(Resource): @optical.response(404, 'Error, not found') class DelFLightpath(Resource): @staticmethod - def delete( src, dst, bitrate, o_band_id,flow_id=None): - flow = None - match1=False - ob_id=None + def delete( src, dst, bitrate, o_band_id, flow_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.db_flows[flow_id]["is_active"] = False - if flow_id in rsa.optical_bands[ob_id]["served_lightpaths"].remove: + if flow_id in rsa.optical_bands[ob_id]["served_lightpaths"]: 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"] @@ -164,34 +163,29 @@ class DelFLightpath(Resource): 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 : - + 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 - + flow = rsa.optical_bands[o_band_id] + match1 = flow["src"] == src and flow["dst"] == dst and flow["bitrate"] + 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"] + 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"].remove: + # if flow_id in rsa.optical_bands[ob_id]["served_lightpaths"]: # 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"] @@ -355,6 +349,7 @@ class GetTopology(Resource): print(rsa.init_link_slots2()) return "ok", 200 except Exception as e: + LOGGER.exception('Error in GetTopology') print(f"err {e}") return "Error", 400 diff --git a/src/opticalcontroller/RSA.py b/src/opticalcontroller/RSA.py index 61a74edbecc2ac635398ee8482800514295e9473..13f6e08106a5c18adc6745f9f33ca47a2ea8a4df 100644 --- a/src/opticalcontroller/RSA.py +++ b/src/opticalcontroller/RSA.py @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -import dijsktra -from tools import * -from variables import * +from opticalcontroller.dijkstra import Graph, shortest_path +from opticalcontroller.tools import * +from opticalcontroller.variables import * class RSA(): @@ -83,7 +83,7 @@ class RSA(): return "{},{},{}".format(self.c_slot_number, self.l_slot_number, self.s_slot_number) def initGraph(self): - self.g = dijsktra.Graph() + self.g = Graph() for n in self.nodes_dict: self.g.add_vertex(n) for l in self.links_dict["optical_links"]: @@ -100,7 +100,7 @@ class RSA(): def initGraph2(self): - self.g = dijsktra.Graph() + self.g = Graph() for n in self.nodes_dict: self.g.add_vertex(n) @@ -117,7 +117,7 @@ class RSA(): self.g.printGraph() def compute_path(self, src, dst): - path = dijsktra.shortest_path(self.g, self.g.get_vertex(src), self.g.get_vertex(dst)) + path = shortest_path(self.g, self.g.get_vertex(src), self.g.get_vertex(dst)) print("INFO: Path from {} to {} with distance: {}".format(src, dst, self.g.get_vertex(dst).get_distance())) if debug: print(path) @@ -266,18 +266,8 @@ 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: @@ -293,20 +283,10 @@ 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() @@ -316,8 +296,8 @@ class RSA(): 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) @@ -368,7 +348,7 @@ class RSA(): # 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, link=l) + self.restore_link(fib, slots, band) if debug: print(fib[band]) ''' @@ -422,7 +402,7 @@ class RSA(): 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_2 fib: {fib} , slots {slots} , band {band} ") + 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 @@ -446,7 +426,7 @@ class RSA(): # 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, link=l) + self.restore_link(fib, slots, band) if debug: print(fib[band]) ''' @@ -511,8 +491,7 @@ 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_2(fib,slots,band,link) + self.update_link(fib, slots, band) break print("INFO: Path forward computation completed") return fiber_list @@ -1234,5 +1213,4 @@ 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/dijsktra.py b/src/opticalcontroller/dijkstra.py similarity index 100% rename from src/opticalcontroller/dijsktra.py rename to src/opticalcontroller/dijkstra.py diff --git a/src/opticalcontroller/tools.py b/src/opticalcontroller/tools.py index 08441f5c0010de1be503ee307410431ddfee54ab..7aa462a9110e641c1b40c7c61d304b1a19ca4c47 100644 --- a/src/opticalcontroller/tools.py +++ b/src/opticalcontroller/tools.py @@ -14,10 +14,10 @@ import json import numpy as np -from variables import * 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): @@ -261,65 +261,5 @@ 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] - print(f"band {band}") - print(f"corrected_slots_before {corrected_slots}") - if (len(key_list) < 20): - corrected_slots=correct_slot(optical_bands[optical_band_id][band]) - - fib={} - print(f"corrected_slots_after {corrected_slots}") - 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(f"invoked from {test}") - print(f"fib updated {fib}") - optical_link = OpticalLink() - linkId = LinkId() - 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 992b11cf3edaaed8211cd70a08452cd091cdf19a..5b9eec527c7c6effcef2c0b0810185dba4fe78bd 100644 --- a/src/service/service/ServiceServiceServicerImpl.py +++ b/src/service/service/ServiceServiceServicerImpl.py @@ -288,6 +288,7 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): # reply with 2 transponders and 2 roadms 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: diff --git a/src/service/service/service_handlers/oc/OCServiceHandler.py b/src/service/service/service_handlers/oc/OCServiceHandler.py index 277080d6589d08e9765b16962b2cc5737c2c50af..3931c97c7eb411b68de894ee534af4fd7d113a92 100644 --- a/src/service/service/service_handlers/oc/OCServiceHandler.py +++ b/src/service/service/service_handlers/oc/OCServiceHandler.py @@ -62,16 +62,20 @@ class OCServiceHandler(_ServiceHandler): # settings = self.__settings_handler.get('/settings') #flow is the new variable that stores input-output relationship - #flows = convert_endpoints_to_flows(endpoints) - flows = endpoints_to_flows(endpoints, bidir, is_opticalband) + 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 = [] - + LOGGER.info(f"flows {flows} ") + LOGGER.info(f"settings {settings} ") + #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) results.append(True) diff --git a/src/service/service/task_scheduler/TaskExecutor.py b/src/service/service/task_scheduler/TaskExecutor.py index 362d72959ff918f60ae6549e7d7714c7768ca242..2698b4afdb1b82a5047889bb8e2f8a47fb6c1a37 100644 --- a/src/service/service/task_scheduler/TaskExecutor.py +++ b/src/service/service/task_scheduler/TaskExecutor.py @@ -218,13 +218,22 @@ class TaskExecutor: service = item if (isinstance(item, ServiceId)): service = self.get_service(item) - class_service_handler = None + service_handler = None service_handler_settings = {} for connection in connections.connections: connection_uuid=connection.connection_id.connection_uuid - if class_service_handler is None: - class_service_handler = self.get_service_handler(connection, service,**service_handler_settings) - if class_service_handler.check_media_channel(connection_uuid): + if service_handler is None: + service_handlers = self.get_service_handlers( + connection, service, **service_handler_settings + ) + # TODO: improve to select different service handlers when needed + # By now, assume a single service handler is retrieved for all the + # device types in the path, i.e., all entries carry the same + # service handler, so we choose the first one retrieved. + if len(service_handlers) < 1: + raise Exception('Unsupported case: {:s}'.format(str(service_handlers))) + service_handler,_ = list(service_handlers.values())[0] + if service_handler.check_media_channel(connection_uuid): return True return False @@ -233,7 +242,16 @@ class TaskExecutor: ) -> bool: service_handler_settings = {} connection_uuid = connection.connection_id.connection_uuid - service_handler_class = self.get_service_handler(connection, service, **service_handler_settings) + classes_service_handlers = self.get_service_handlers( + connection, service, **service_handler_settings + ) + # TODO: improve to select different service handlers when needed + # By now, assume a single service handler is retrieved for all the + # device types in the path, i.e., all entries carry the same + # service handler, so we choose the first one retrieved. + if len(classes_service_handlers) < 1: + raise Exception('Unsupported case: {:s}'.format(str(classes_service_handlers))) + service_handler_class,_ = list(classes_service_handlers.values())[0] return service_handler_class.check_media_channel(connection_uuid) def get_device_controller(self, device : Device) -> Optional[Device]: diff --git a/src/service/service/task_scheduler/TaskScheduler.py b/src/service/service/task_scheduler/TaskScheduler.py index c2d8423156fd145a262108701860aeb17df3e6f9..a522aad4ba5a37ccd064ce35742732a08abcf6c7 100644 --- a/src/service/service/task_scheduler/TaskScheduler.py +++ b/src/service/service/task_scheduler/TaskScheduler.py @@ -203,9 +203,16 @@ class TasksScheduler: for connection in connections.connections: connection_uuid = connection.connection_id.connection_uuid.uuid if class_service_handler is None: - class_service_handler = self._executor.get_service_handler( + classes_service_handlers = self._executor.get_service_handlers( connection, service, **service_handler_settings ) + # TODO: improve to select different service handlers when needed + # By now, assume a single service handler is retrieved for all the + # device types in the path, i.e., all entries carry the same + # service handler, so we choose the first one retrieved. + if len(classes_service_handlers) < 1: + raise Exception('Unsupported case: {:s}'.format(str(classes_service_handlers))) + class_service_handler,_ = list(classes_service_handlers.values())[0] if class_service_handler.check_media_channel(connection_uuid): has_media_channel = True else : diff --git a/src/service/service/task_scheduler/tasks/Task_OpticalConnectionDeconfigure.py b/src/service/service/task_scheduler/tasks/Task_OpticalConnectionDeconfigure.py index b1afdeef19d6526d6f52b05924b5b77a8049da8f..a624e81b58d4d2e7f6f1ad109f99a0eb1c73bb63 100644 --- a/src/service/service/task_scheduler/tasks/Task_OpticalConnectionDeconfigure.py +++ b/src/service/service/task_scheduler/tasks/Task_OpticalConnectionDeconfigure.py @@ -48,7 +48,16 @@ class Task_OpticalConnectionDeconfigure(_Task): service = self._task_executor.get_service(connection.service_id) errors = [] service_handler_settings = {} - service_handler = self._task_executor.get_service_handler(connection, service, **service_handler_settings) + service_handlers = self._task_executor.get_service_handlers( + connection, service, **service_handler_settings + ) + # TODO: improve to select different service handlers when needed + # By now, assume a single service handler is retrieved for all the + # device types in the path, i.e., all entries carry the same + # service handler, so we choose the first one retrieved. + if len(service_handlers) < 1: + raise Exception('Unsupported case: {:s}'.format(str(service_handlers))) + service_handler,_ = list(service_handlers.values())[0] endpointids_to_delete = endpointids_to_raw(connection.path_hops_endpoint_ids) connection_uuid = connection.connection_id.connection_uuid.uuid diff --git a/src/tests/ofc24/.gitlab-ci.yml b/src/tests/ofc24/.gitlab-ci.yml index af4e1aaa6b98beb21a85a4746f61cf9d4916dba7..29428e0b23c61fa6d5467259597b2e279332ff39 100644 --- a/src/tests/ofc24/.gitlab-ci.yml +++ b/src/tests/ofc24/.gitlab-ci.yml @@ -136,9 +136,7 @@ end2end_test ofc24: - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/deviceservice -c server - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/pathcompservice -c frontend - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/serviceservice -c server - - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/sliceservice -c server - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/nbiservice -c server - - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/webuiservice -c server - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/opticalcontrollerservice -c server - if docker ps -a | grep ${TEST_NAME}; then docker rm -f ${TEST_NAME}; fi diff --git a/src/tests/ofc24/Dockerfile b/src/tests/ofc24/Dockerfile index 734cf6233e3f9e6b3839c5a054143de57c0a0e92..5246a5a31d2840e24f0b64b17079bcf74e800d9f 100644 --- a/src/tests/ofc24/Dockerfile +++ b/src/tests/ofc24/Dockerfile @@ -92,8 +92,9 @@ export PYTHONPATH=/var/teraflow pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_bootstrap.py --junitxml=/opt/results/report_bootstrap.xml pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_create_service_unidir.py --junitxml=/opt/results/report_create_service_unidir.xml pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_delete_service_unidir.py --junitxml=/opt/results/report_delete_service_unidir.xml -pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_create_service_bidir.py --junitxml=/opt/results/report_create_service_bidir.xml -pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_delete_service_bidir.py --junitxml=/opt/results/report_delete_service_bidir.xml +# TODO: review bidirectional code. For now, test is disabled til reworked. +#pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_create_service_bidir.py --junitxml=/opt/results/report_create_service_bidir.xml +#pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_delete_service_bidir.py --junitxml=/opt/results/report_delete_service_bidir.xml pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_cleanup.py --junitxml=/opt/results/report_cleanup.xml EOF RUN chmod ug+x ./run_tests.sh diff --git a/src/tests/ofc24/deploy_specs.sh b/src/tests/ofc24/deploy_specs.sh index 4cd0e7fa0c1d64ff965bcf4b66042fe3fc8fd557..97a4673d8671a67da119faf05527b799f15dab64 100755 --- a/src/tests/ofc24/deploy_specs.sh +++ b/src/tests/ofc24/deploy_specs.sh @@ -21,7 +21,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 load_generator" -export TFS_COMPONENTS="context device pathcomp service slice nbi webui" +export TFS_COMPONENTS="context device pathcomp service nbi" # Uncomment to activate Monitoring #export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring" @@ -110,7 +110,7 @@ export CRDB_DEPLOY_MODE="single" export CRDB_DROP_DATABASE_IF_EXISTS="YES" # Disable flag for re-deploying CockroachDB from scratch. -export CRDB_REDEPLOY="" +export CRDB_REDEPLOY="YES" # ----- NATS ------------------------------------------------------------------- diff --git a/src/tests/ofc24/descriptors/service-bidir.json b/src/tests/ofc24/descriptors/service-bidir.json index 05547a19d2d375d2a8202266b1a1a5ffcfe46eb6..8cb587ccf8c31ed994f7e81c6d58331d4b7430cb 100755 --- a/src/tests/ofc24/descriptors/service-bidir.json +++ b/src/tests/ofc24/descriptors/service-bidir.json @@ -3,13 +3,13 @@ { "service_id": { "context_id": {"context_uuid": {"uuid": "admin"}}, - "service_uuid": {"uuid": "optical-connection"} + "service_uuid": {"uuid": "optical-connection-bidir"} }, "service_type": 6, "service_status": {"service_status": 1}, "service_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "T1"}}, "endpoint_uuid": {"uuid": "1"}}, - {"device_id": {"device_uuid": {"uuid": "T2"}}, "endpoint_uuid": {"uuid": "6"}} + {"device_id": {"device_uuid": {"uuid": "T1.1"}}, "endpoint_uuid": {"uuid": "1"}}, + {"device_id": {"device_uuid": {"uuid": "T2.1"}}, "endpoint_uuid": {"uuid": "6"}} ], "service_constraints": [ {"custom": {"constraint_type": "bandwidth[gbps]", "constraint_value": "100.0"}}, diff --git a/src/tests/ofc24/descriptors/service-unidir.json b/src/tests/ofc24/descriptors/service-unidir.json index d9b4e88479c17aaf03f65d08b8d4b0ca22121e74..e96f10b156948568945def99ea91a4d704ffd4d5 100755 --- a/src/tests/ofc24/descriptors/service-unidir.json +++ b/src/tests/ofc24/descriptors/service-unidir.json @@ -3,13 +3,13 @@ { "service_id": { "context_id": {"context_uuid": {"uuid": "admin"}}, - "service_uuid": {"uuid": "optical-connection"} + "service_uuid": {"uuid": "optical-connection-unidir"} }, "service_type": 6, "service_status": {"service_status": 1}, "service_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "T1"}}, "endpoint_uuid": {"uuid": "1"}}, - {"device_id": {"device_uuid": {"uuid": "T2"}}, "endpoint_uuid": {"uuid": "6"}} + {"device_id": {"device_uuid": {"uuid": "T1.2"}}, "endpoint_uuid": {"uuid": "1"}}, + {"device_id": {"device_uuid": {"uuid": "T2.2"}}, "endpoint_uuid": {"uuid": "6"}} ], "service_constraints": [ {"custom": {"constraint_type": "bandwidth[gbps]", "constraint_value": "100.0"}}, diff --git a/src/tests/ofc24/tests/test_functional_bootstrap.py b/src/tests/ofc24/tests/test_functional_bootstrap.py index c82e751f9a956f92ff5892aaa4d7bb2deff7d8f3..ad76532d5ed76f059bafe4ba7ee997faf0d0428c 100644 --- a/src/tests/ofc24/tests/test_functional_bootstrap.py +++ b/src/tests/ofc24/tests/test_functional_bootstrap.py @@ -16,6 +16,7 @@ import logging, os, time from common.Constants import DEFAULT_CONTEXT_NAME from common.proto.context_pb2 import ContextId, DeviceOperationalStatusEnum, Empty from common.tools.descriptor.Loader import DescriptorLoader, check_descriptor_load_results, validate_empty_scenario +from common.tools.grpc.Tools import grpc_message_to_json, grpc_message_to_json_string from common.tools.object_factory.Context import json_context_id from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient @@ -59,9 +60,15 @@ def test_scenario_devices_enabled( response = context_client.ListDevices(Empty()) num_devices = len(response.devices) num_devices_enabled = 0 + disabled_devices = list() for device in response.devices: - if device.device_operational_status != DEVICE_OP_STATUS_ENABLED: continue - num_devices_enabled += 1 + if device.device_operational_status == DEVICE_OP_STATUS_ENABLED: + num_devices_enabled += 1 + else: + disabled_devices.append(grpc_message_to_json(device)) LOGGER.info('Num Devices enabled: {:d}/{:d}'.format(num_devices_enabled, num_devices)) num_retry += 1 + if num_devices_enabled != num_devices: + LOGGER.info('Disabled Devices: {:s}'.format(str(disabled_devices))) + LOGGER.info('Devices: {:s}'.format(grpc_message_to_json_string(response))) assert num_devices_enabled == num_devices diff --git a/src/tests/ofc24/tests/test_functional_delete_service_unidir.py b/src/tests/ofc24/tests/test_functional_delete_service_unidir.py index fdfcdec29498b6c7ca92dff3505afec2c9089ec2..8e23cd10963a018aed2599e88eb33912335e0e97 100644 --- a/src/tests/ofc24/tests/test_functional_delete_service_unidir.py +++ b/src/tests/ofc24/tests/test_functional_delete_service_unidir.py @@ -69,9 +69,30 @@ def test_service_removal_unidir( assert len(context_service_uuids) == 1 context_uuid, service_uuid = set(context_service_uuids).pop() - # Delete Service + # NOTE: For optical bands, the service needs to be removed twice, + # first time for the lightpath, second for the optical band. + + # Check there is exactly 1 service + response = context_client.ListServices(ADMIN_CONTEXT_ID) + LOGGER.warning('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) + assert len(response.services) == 1 + + # Delete Service (lightpath) + service_client.DeleteService(ServiceId(**json_service_id(service_uuid, json_context_id(context_uuid)))) + + # Check there is exactly 1 service + response = context_client.ListServices(ADMIN_CONTEXT_ID) + LOGGER.warning('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) + assert len(response.services) == 1 + + # Delete Service (optical band) service_client.DeleteService(ServiceId(**json_service_id(service_uuid, json_context_id(context_uuid)))) + # Check there are no services + response = context_client.ListServices(ADMIN_CONTEXT_ID) + LOGGER.warning('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) + assert len(response.services) == 0 + # Verify the scenario has no services/slices response = context_client.GetContext(ADMIN_CONTEXT_ID) assert len(response.service_ids) == 0 diff --git a/src/webui/service/__init__.py b/src/webui/service/__init__.py index 4c39e3c033717d1e6e48120a5ebf27fd327ecac9..5b19d632b29b62b666de223ac4ca133dcdb4db05 100644 --- a/src/webui/service/__init__.py +++ b/src/webui/service/__init__.py @@ -14,12 +14,11 @@ import json from typing import List, Tuple, Union -from flask import Flask, request, session +from flask import Flask, session from flask_healthz import healthz, HealthError from common.tools.grpc.Tools import grpc_message_to_json from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient -from qkd_app.client.QKDAppClient import QKDAppClient from common.Settings import ( is_deployed_bgpls, is_deployed_load_gen, is_deployed_optical, is_deployed_policy, is_deployed_qkd_app, is_deployed_slice @@ -42,10 +41,6 @@ def readiness(): device_client = DeviceClient() device_client.connect() device_client.close() - # DEPENDENCY QKD - qkd_app_client = QKDAppClient() - qkd_app_client.connect() - qkd_app_client.close() except Exception as e: raise HealthError("Can't connect with the service: {:s}".format(str(e))) from e diff --git a/src/webui/service/opticalconfig/routes.py b/src/webui/service/opticalconfig/routes.py index 79a487a4e80d96d3f6a6bab1e813202e6679ee66..9fa60fb4ef3c07bee9ecfc4617cc9f885e63380c 100644 --- a/src/webui/service/opticalconfig/routes.py +++ b/src/webui/service/opticalconfig/routes.py @@ -20,15 +20,15 @@ from flask import ( from common.proto.context_pb2 import ( Empty, OpticalConfig, OpticalConfigId, OpticalConfigList ) +from common.tools.context_queries.OpticalConfig import opticalconfig_get_uuid +from common.DeviceTypes import DeviceTypeEnum from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient from service.client.ServiceClient import ServiceClient from slice.client.SliceClient import SliceClient from .forms import UpdateDeviceForm, AddTrancseiver, UpdateStatusForm -from common.tools.context_queries.OpticalConfig import opticalconfig_get_uuid - -opticalconfig = Blueprint('opticalconfig', __name__,url_prefix="/opticalconfig") +opticalconfig = Blueprint('opticalconfig', __name__, url_prefix="/opticalconfig") context_client = ContextClient() device_client = DeviceClient() @@ -42,7 +42,6 @@ DESCRIPTOR_LOADER_NUM_WORKERS = 10 @opticalconfig.get("/") def home() : list_config = [] - channels_num = 0 if 'context_uuid' not in session or 'topology_uuid' not in session: flash("Please select a context!", "warning") return redirect(url_for("main.home")) @@ -54,9 +53,12 @@ def home() : for configs in opticalConfig_list.opticalconfigs: value = json.loads(configs.config) if type(configs.config)==str else configs.config config_type = value["type"] - if 'channels' in value: - channels_num = len(value['channels']) - value["channels_number"] = channels_num + if config_type != DeviceTypeEnum.OPEN_ROADM._value_ : + if 'channels' in value: + value["channels_number"] = len(value['channels']) + else: + if 'interfaces' in value: + value["channels_number"] = len(value['interfaces']) # value['operationalMode']=value['operational-mode'] # value['targetOutputPower']=value['target-output-power'] value['opticalconfig_id']=configs.opticalconfig_id @@ -78,41 +80,58 @@ def show_details(config_uuid): if (response and response.opticalconfig_id.opticalconfig_uuid !=''): opticalConfig = OpticalConfig() opticalConfig.CopyFrom(response) - + device_name = "" config = json.loads(opticalConfig.config) if "device_name" in config: device_name = config["device_name"] - config_type = config["type"] - if config_type == 'optical-transponder': - if 'channels' in config: - for channel in config['channels'] : - new_config = { - "name" : channel['name'], - 'operationalMode' : channel['operational-mode'] if 'operational-mode' in channel else '', - 'targetOutputPower': channel['target-output-power'] if 'target-output-power' in channel else '', - "frequency" : channel['frequency'] if 'frequency' in channel else '', - 'line_port' : channel["line-port"] if 'line-port' in channel else '', - "status" : channel['status'] if 'status' in channel else "", - } - device_details.append(new_config) - - if config_type == 'optical-roadm': - if 'channels' in config: - for channel in config['channels'] : - new_config = { - "band_name" : channel['band_name'] if 'band_name' in channel else None, - 'type' : channel['type'] if 'type' in channel else '', - 'src_port' : channel['src_port'] if 'src_port' in channel else '', - 'dest_port' : channel['dest_port'] if 'dest_port' in channel else '', - "lower_frequency" : channel['lower_frequency'] if 'lower_frequency' in channel else '', - "upper_frequency" : channel['upper_frequency'] if 'upper_frequency' in channel else '', - "status" : channel['status'] if 'status' in channel else "", - 'optical_band_parent' : channel['optical_band_parent'] if 'optical_band_parent' in channel else '', - 'channel_index' : channel['channel_index'] if 'channel_index' in channel else '', - } - device_details.append(new_config) + config_type = config["type"] + if config_type == DeviceTypeEnum.OPTICAL_TRANSPONDER._value_: + LOGGER.info("config details from show detail %s",config) + if 'channels' in config: + for channel in config['channels'] : + new_config={} + new_config["name"]=channel['name'] + new_config['operationalMode']=channel['operational-mode'] if 'operational-mode' in channel else '' + new_config['targetOutputPower']=channel['target-output-power'] if 'target-output-power' in channel else '' + new_config["frequency"]=channel['frequency'] if 'frequency' in channel else '' + new_config['line_port']=channel["line-port"] if 'line-port' in channel else '' + new_config["status"] = channel['status'] if 'status' in channel else "" + device_details.append(new_config) + + if config_type == DeviceTypeEnum.OPTICAL_ROADM._value_: + LOGGER.info("config details from show detail %s",config) + if 'channels' in config: + for channel in config['channels'] : + new_config={} + new_config["band_name"]=channel['band_name'] if 'band_name' in channel else None + new_config['type']=channel['type'] if 'type' in channel else '' + new_config['src_port']=channel['src_port'] if 'src_port' in channel else '' + new_config['dest_port']=channel['dest_port'] if 'dest_port' in channel else '' + new_config["lower_frequency"]=channel['lower_frequency'] if 'lower_frequency' in channel else '' + new_config["upper_frequency"]=channel['upper_frequency'] if 'upper_frequency' in channel else '' + new_config["status"] = channel['status'] if 'status' in channel else "" + new_config['optical_band_parent']= channel['optical_band_parent'] if 'optical_band_parent' in channel else '' + new_config['channel_index']= channel['channel_index'] if 'channel_index' in channel else '' + device_details.append(new_config) + + if config_type == DeviceTypeEnum.OPEN_ROADM._value_: + if 'interfaces' in config : + for interface in config["interfaces"]: + new_config={} + new_config["name"]=interface["name"] if "name" in interface else '' + new_config["administrative_state"]=interface[ "administrative_state"] + new_config["circuit_pack_name"]=interface["circuit_pack_name"] + new_config["port"]=interface["port"] + new_config["interface_list"]=interface["interface_list"] + new_config["frequency"]=interface["frequency"] + new_config["width"]=interface[ "width"] + new_config["type"]=interface["type"] + device_details.append(new_config) + + LOGGER.info("device details %s",device_details) + return render_template('opticalconfig/details.html', device=device_details,config_id=config_uuid,device_name=device_name,type=config_type) return render_template( 'opticalconfig/details.html', device=device_details, config_id=config_uuid, @@ -327,3 +346,62 @@ def update_status (config_uuid,channel_name): 'opticalconfig/update_status.html', form=form, channel_name=channel_name, submit_text='Update Device Status' ) + +@opticalconfig.route('/configure_openroadm', methods=['POST']) +def update_openroadm(): + if (request.method == 'POST'): + ports_list= [] + data = request.get_json() + LOGGER.info(f"data {data}") + devices=data.get('devices') + LOGGER.info(f"devices {devices}") + myResponse =[] + status_code='' + config={} + target_interface={} + for device in devices : + frequency = device.get("frequency") + width= device.get("width") + ports=device.get('ports') + device_name=device.get("device_name") + LOGGER.info(f"device from post {device}") + target_interface["frequency"]=frequency + target_interface["width"]=width + target_interface["administrative-state"]="inService" + target_interface["config_type"]=device.get("config_type") + + if (device_name): + opticalconfig_uuid = opticalconfig_get_uuid(device_name=device_name) + opticalconfigId=OpticalConfigId() + opticalconfigId.opticalconfig_uuid=opticalconfig_uuid + context_client.connect() + opticalconfig = context_client.SelectOpticalConfig(opticalconfigId) + context_client.close() + config =json.loads(opticalconfig.config) + for port in ports : + ports_list.append({ + "supporting-circuit-pack-name":port["circuit_pack"], + 'supporting-interface-list':port["interface_list"], + 'supporting-port':port["port"] + }) + target_interface["ports"]=ports_list + config["new_config"]=target_interface + opticalconfig.config =json.dumps(config) + try: + device_client.connect() + device_client.ConfigureOpticalDevice(opticalconfig) + device_client.close() + myResponse.append(f"device {device_name} port {port} is updated successfully") + status_code = 200 + except Exception as e: # pylint: disable=broad-except + myResponse.append(f"Problem updating the device. {e}") + status_code = 500 + break + else : + myResponse.append(f"requested device {device_name} is not existed") + status_code = 400 + break + + response=make_response(f'{myResponse}') + response.status_code=status_code + return response diff --git a/src/webui/service/templates/opticalconfig/details.html b/src/webui/service/templates/opticalconfig/details.html index 70b17331097f24733729345d252ce1090e568018..f74538eed82789514a4ef6e52f12c6a4593cf0f9 100644 --- a/src/webui/service/templates/opticalconfig/details.html +++ b/src/webui/service/templates/opticalconfig/details.html @@ -14,113 +14,129 @@ limitations under the License. --> - - {% extends 'base.html' %} {% block content %} -

Optical Configurations

- {% if device %} -
- Device ID: -
{{config_id}}
-
-
-
-
- -
- +
+ Device ID: +
{{config_id}}
+
+
+
+
+
+ +
+
+ + +
-
- - +
+
+ Device Name: + {{device_name}}
-
-
- Device Name: - {{device_name}} -
- -
- {% if type == 'optical-transponder' %} - - - - - - - - - - - - - - {% for channel in device %} - - - - - - - - - {% endfor %} - -
channel nameFrequencyTarget Output PowerOperational ModeLine PortChannel Status
{{channel.name.index}}{{channel.frequency}} {{channel.targetOutputPower}}{{channel.operationalMode}}{{channel.line_port}} {{channel.status}}
- {%else%} - - - - - - - - - - - - - - - - - - {% for channel in device %} - - - - - - - - - - - - - {% endfor %} - - -
Channel IndexOptical Band ParentBand NameLower FrequencyUpper Frequencytype Source PortDestination PortChannel Status
{{channel.channel_index}}{{channel.optical_band_parent}} {{channel.band_name}}{{channel.lower_frequency}}{{channel.upper_frequency}} {{channel.type}}{{channel.src_port}}{{channel.dest_port}} {{channel.status}}
- - {% endif%} - -
+ {% if type == 'optical-transponder' %} + + + + + + + + + + + + + {% for channel in device %} + + + + + + + + + {% endfor %} + +
channel nameFrequencyTarget Output PowerOperational ModeLine PortChannel Status
{{channel.name.index}}{{channel.frequency}}{{channel.targetOutputPower}}{{channel.operationalMode}}{{channel.line_port}}{{channel.status}}
+ {% elif type == 'openroadm' %} + + + + + + + + + + + + + + + {% for channel in device %} + + + + + + + + + + + {% endfor %} + +
name typeadministrative statecircuit pack nameportinterface listfrequencywidth
{{channel.name}}{{channel.type}}{{channel.administrative_state}}{{channel.circuit_pack_name}}{{channel.port}}{{channel.interface_list}}{{channel.frequency}}{{channel.width}}
+ {% else %} + + + + + + + + + + + + + + + + {% for channel in device %} + + + + + + + + + + + + {% endfor %} + +
Channel IndexOptical Band ParentBand NameLower FrequencyUpper FrequencytypeSource PortDestination PortChannel Status
{{channel.channel_index}}{{channel.optical_band_parent}}{{channel.band_name}}{{channel.lower_frequency}}{{channel.upper_frequency}}{{channel.type}}{{channel.src_port}}{{channel.dest_port}}{{channel.status}}
+ {% endif%} +
{% else %}
diff --git a/src/webui/service/templates/qkd_app/home.html b/src/webui/service/templates/qkd_app/home.html index 39b43ecc4b9b71e03d27bf080ae6c7be2bf1a90d..d715562f91be568f8d2392eace2d5928345e6902 100644 --- a/src/webui/service/templates/qkd_app/home.html +++ b/src/webui/service/templates/qkd_app/home.html @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - + {% extends 'base.html' %} {% block content %}