diff --git a/src/context/service/database/OpticalConfig.py b/src/context/service/database/OpticalConfig.py index 07d20434357be337d5a70878c5f1c17f280b7eea..89ad3253caeaac5aff06acbedf723b0ba994aca9 100644 --- a/src/context/service/database/OpticalConfig.py +++ b/src/context/service/database/OpticalConfig.py @@ -19,8 +19,8 @@ from sqlalchemy.engine import Engine from sqlalchemy.orm import Session, sessionmaker from sqlalchemy_cockroachdb import run_transaction from common.proto.context_pb2 import OpticalConfig, OpticalConfigId , Empty , EventTypeEnum -from .models.OpticalConfigModel import OpticalConfigModel , OpticalChannelModel -from context.service.database.uuids.OpticalConfig import channel_get_uuid , opticalconfig_get_uuid +from .models.OpticalConfigModel import OpticalConfigModel , TransponderTypeModel ,OpticalChannelModel +from context.service.database.uuids.OpticalConfig import channel_get_uuid , opticalconfig_get_uuid ,transponder_get_uuid from .Events import notify_event_opticalconfig LOGGER = logging.getLogger(__name__) @@ -36,7 +36,7 @@ def get_opticalconfig(db_engine : Engine): optical_config = OpticalConfig() optical_config.config = json.dumps(obj.dump()) ids_obj = obj.dump_id() - LOGGER.info(f"ids {ids_obj}") + optical_config.opticalconfig_id.opticalconfig_uuid = ids_obj["opticalconfig_uuid"] optical_config.device_id.device_uuid.uuid=ids_obj["device_uuid"] optical_configs.append(optical_config) @@ -49,39 +49,64 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig): opticalconfig_id = OpticalConfigId() device_id = request.device_id device_uuid = request.device_id.device_uuid.uuid + channels = [] + transponder=[] + OpticalConfig_data = [] + config_type=None + opticalconfig_uuid =opticalconfig_get_uuid(device_id) + LOGGER.info(f"cofigy_type {request.config}") if request.config: - channels = [] - transceivers = [] config = json.loads(request.config) - opticalconfig_uuid =opticalconfig_get_uuid(device_id) - if 'transceivers' in config and len(config['transceivers']['transceiver']) > 0: - transceivers = [transceiver for transceiver in config['transceivers']['transceiver']] + if "type" in config: + + config_type= config["type"] + if config_type == "optical-transponder": + + transceivers = [] - if 'channels' in config and len(config['channels']) > 0: - #channels = [channel['name']['index'] for channel in config['channels']] - - for channel_params in config['channels']: - channels.append( - { - "opticalconfig_uuid":opticalconfig_uuid, - "channel_uuid":channel_get_uuid(channel_params['name']['index']), - "channel_name" : channel_params['name']['index'], - "frequency" : int(channel_params["frequency"]) if "frequency" in channel_params else 0, - "operational_mode" : int(channel_params["operational-mode"]) if "operational-mode" in channel_params else 0, - "target_output_power" : channel_params["target-output-power"] if "target-output-power" in channel_params else '', - "status":channel_params["status"] if "status" in channel_params else "" - } - ) - + + + if 'transceivers' in config['transponder'] and len(config['transponder']['transceivers']['transceiver']) > 0: + transceivers = [transceiver for transceiver in config['transponder'] ['transceivers']['transceiver']] + + if 'channels' in config['transponder'] and len(config['transponder']['channels']) > 0: + #channels = [channel['name']['index'] for channel in config['channels']] + + for channel_params in config['transponder']['channels']: + channels.append( + { + # "opticalconfig_uuid":opticalconfig_uuid, + "transponder_uuid":transponder_get_uuid(device_id), + "channel_uuid":channel_get_uuid(channel_params['name']['index']), + "channel_name" : channel_params['name']['index'], + "frequency" : int(channel_params["frequency"]) if "frequency" in channel_params else 0, + "operational_mode" : int(channel_params["operational-mode"]) if "operational-mode" in channel_params else 0, + "target_output_power" : channel_params["target-output-power"] if "target-output-power" in channel_params else '', + "status":channel_params["status"] if "status" in channel_params else "" + } + ) + + transponder.append({ + "transponder_uuid":transponder_get_uuid(device_id), + "transcievers":transceivers, + "interfaces":None, + "opticalconfig_uuid":opticalconfig_uuid, + + + }) + + OpticalConfig_data.append( { "opticalconfig_uuid":opticalconfig_uuid, - "transcievers" : transceivers, - "interfaces" :"", + # "transcievers" : transceivers, + # "interfaces" :"", "channel_namespace" : config.get("channel_namespace",None), "endpoints" : [json.dumps(endpoint) for endpoint in config.get("endpoints",[])], - "device_uuid": device_uuid} + "device_uuid": device_uuid, + "type":config_type + } ) @@ -97,24 +122,38 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig): ) stmt = stmt.returning(OpticalConfigModel.opticalconfig_uuid) opticalconfig_id = session.execute(stmt).fetchone() - if (len(channels)>0) : + if config_type == 'optical-transponder': + if (len(transponder)>0): + stmt = insert(TransponderTypeModel).values(transponder) - stmt = insert(OpticalChannelModel).values(channels) - stmt = stmt.on_conflict_do_update( - index_elements=[OpticalChannelModel.channel_uuid , OpticalConfigModel.opticalconfig_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, + index_elements=[TransponderTypeModel.transponder_uuid], + set_=dict( + transcievers= stmt.excluded.transcievers , + ) ) - - ) - stmt = stmt.returning(OpticalChannelModel.channel_uuid) - opticalChannel_id = session.execute(stmt).fetchone() - LOGGER.info(f"added successfully {opticalChannel_id}") + stmt = stmt.returning(TransponderTypeModel.transponder_uuid) + transponder_id = session.execute(stmt).fetchone() + + if (len(channels)>0) : + + stmt = insert(OpticalChannelModel).values(channels) + + stmt = stmt.on_conflict_do_update( + index_elements=[OpticalChannelModel.channel_uuid ], + set_=dict( + channel_name= stmt.excluded.channel_name , + frequency = stmt.excluded.frequency, + operational_mode=stmt.excluded.operational_mode, + target_output_power=stmt.excluded.target_output_power, + + ) + + ) + stmt = stmt.returning(OpticalChannelModel.channel_uuid) + opticalChannel_id = session.execute(stmt).fetchone() + LOGGER.info(f"added successfully {opticalChannel_id}") opticalconfig_id = run_transaction(sessionmaker(bind=db_engine), callback) return {'opticalconfig_uuid': opticalconfig_id} diff --git a/src/context/service/database/models/OpticalConfigModel.py b/src/context/service/database/models/OpticalConfigModel.py index cd819bf861b6c9e0b9a70a5f29d777515dd4aab8..e6a6fca0a61e42ced27dd7b33ec1b46dc34ebbcf 100644 --- a/src/context/service/database/models/OpticalConfigModel.py +++ b/src/context/service/database/models/OpticalConfigModel.py @@ -21,13 +21,16 @@ from ._Base import _Base class OpticalConfigModel(_Base): __tablename__ = 'optical_config' opticalconfig_uuid = Column(String, primary_key=True) - - transcievers = Column(ARRAY(String), nullable=True) - interfaces = Column(String, nullable=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) + - channels = relationship("OpticalChannelModel") + #channels = relationship("OpticalChannelModel") + transponders=relationship("TransponderTypeModel") device_uuid = Column(ForeignKey("device.device_uuid",ondelete="CASCADE"),index=True ,nullable=False) @@ -43,26 +46,56 @@ class OpticalConfigModel(_Base): def dump(self): return { - "channels" : [channel.dump() for channel in self.channels], - "transceivers" : {"transceiver": [transciever for transciever in self.transcievers]}, - "interfaces" : {"interface":json.loads(self.interfaces) if self.interfaces else ''}, + # "channels" : [channel.dump() for channel in self.channels], + # "transceivers" : {"transceiver": [transciever for transciever in self.transcievers]}, + # "interfaces" : {"interface":json.loads(self.interfaces) if self.interfaces else ''}, "channel_namespace" : self.channel_namespace, "endpoints" : [json.loads(endpoint) for endpoint in self.endpoints if endpoint], - "device_name": self.device.device_name + "device_name" : self.device.device_name, + "transponder" : [transponer.dump() for transponer in self.transponders if self.type =="optical-transponder" ], + "type" : self.type } - + +class TransponderTypeModel (_Base): + + __tablename__ = 'transponder_type' + transponder_uuid = Column(String, primary_key=True) + + transcievers = Column(ARRAY(String), nullable=True) + interfaces = Column(String, nullable=True) + channels = relationship("OpticalChannelModel") + + opticalconfig_uuid = Column(ForeignKey('optical_config.opticalconfig_uuid', ondelete='CASCADE' ),index=True ,nullable=False) + opticalconfig = relationship('OpticalConfigModel', back_populates='transponders') + + def dump_id (self): + return { + "transponder_uuid":self.transponder_uuid + } + + def dump (self): + return { + #"channels" : [channel.dump() for channel in self.channels], + "transceivers" : {"transceiver": [transciever for transciever in self.transcievers]}, + "interfaces" : {"interface":json.loads(self.interfaces) if self.interfaces else ''}, + "trasponder_uuid" : self.dump_id() + } class OpticalChannelModel(_Base): - __tablename__ = 'optical_channel' - channel_uuid = Column(String, primary_key=True) - channel_name = Column (String,nullable=True) - frequency = Column(Integer, nullable=True) - operational_mode = Column(Integer, nullable=True) - status = Column(String , nullable=True) - target_output_power = Column(String, nullable=True) - opticalconfig_uuid = Column(ForeignKey('optical_config.opticalconfig_uuid', ondelete='CASCADE' ), primary_key=True) - opticalconfig = relationship('OpticalConfigModel', back_populates='channels') + __tablename__ = 'optical_channel' + channel_uuid = Column(String, primary_key=True) + + channel_name = Column (String,nullable=True) + frequency = Column(Integer, nullable=True) + operational_mode = Column(Integer, nullable=True) + status = Column(String , nullable=True) + target_output_power = Column(String, nullable=True) + + transponder_uuid = Column(ForeignKey('transponder_type.transponder_uuid', ondelete='CASCADE' ),nullable=False) + transponder = relationship('TransponderTypeModel',back_populates='channels') + # opticalconfig_uuid = Column(ForeignKey('optical_config.opticalconfig_uuid', ondelete='CASCADE' ), primary_key=True) + # opticalconfig = relationship('OpticalConfigModel', back_populates='channels') def dump_id (self ): return { "channel_uuid":self.channel_uuid diff --git a/src/context/service/database/uuids/OpticalConfig.py b/src/context/service/database/uuids/OpticalConfig.py index 4bff2fc35c0ab71b1814346ce23619764df23007..203c424823f910379b399c85ae7caccf417c638f 100644 --- a/src/context/service/database/uuids/OpticalConfig.py +++ b/src/context/service/database/uuids/OpticalConfig.py @@ -2,6 +2,7 @@ from common.method_wrappers.ServiceExceptions import InvalidArgumentsException from ._Builder import get_uuid_from_string, get_uuid_random from common.proto.context_pb2 import DeviceId + def channel_get_uuid( channel_name :str , allow_random : bool = False ) -> str: @@ -16,6 +17,21 @@ def channel_get_uuid( ], extra_details=['Channel name is required to produce a channel UUID']) +def transponder_get_uuid( + opticalconfig_id :str , allow_random : bool = False +) -> str: + + + if opticalconfig_id is not None: + return get_uuid_from_string(f"{opticalconfig_id}-transponder") + if allow_random: return get_uuid_random() + + raise InvalidArgumentsException([ + ('transponder uuid', opticalconfig_id), + + ], extra_details=['Channel name is required to produce a channel UUID']) + + def opticalconfig_get_uuid ( device_id: DeviceId, allow_random : bool = False) -> str : device_uuid = device_id.device_uuid.uuid if (len(device_uuid)>0): diff --git a/src/device/service/drivers/oc_driver/OCDriver.py b/src/device/service/drivers/oc_driver/OCDriver.py index dde5fe514e108d6fe3866a2d92d13a1fe1405009..0b985e0292ad259d3d3a66e6b83865dc4c051502 100644 --- a/src/device/service/drivers/oc_driver/OCDriver.py +++ b/src/device/service/drivers/oc_driver/OCDriver.py @@ -37,8 +37,8 @@ from .RetryDecorator import retry from context.client.ContextClient import ContextClient from common.proto.context_pb2 import ( OpticalConfig) -from .templates.Tools import roadm_values_extractor, transponder_values_extractor - +from .templates.descovery_tool.transponders import transponder_values_extractor +from .templates.descovery_tool.roadms import roadm_values_extractor ,openroadm_values_extractor DEBUG_MODE = False logging.getLogger('ncclient.manager').setLevel(logging.DEBUG if DEBUG_MODE else logging.WARNING) logging.getLogger('ncclient.transport.ssh').setLevel(logging.DEBUG if DEBUG_MODE else logging.WARNING) @@ -271,17 +271,25 @@ class OCDriver(_Driver): transceivers={} oc_values={} ports_result=[] + oc_values["type"]=self.__type try: + oc_values["transponder"]={} xml_data = self.__netconf_handler.get().data_xml - + logging.info(f"type {self.__type}") if (self.__type == "optical-transponder"): extracted_values=transponder_values_extractor(data_xml=xml_data,resource_keys=transponder_filter_fields,dic=config) transceivers,optical_channels_params,channel_namespace,endpoints,ports_result=extracted_values - 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["transponder"]["channels"]=optical_channels_params + oc_values["transponder"]["transceivers"]=transceivers + oc_values["transponder"]["channel_namespace"]=channel_namespace + oc_values["transponder"]["endpoints"]=endpoints + oc_values["transponder"]["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] + + + else : extracted_values=roadm_values_extractor(data_xml=xml_data,resource_keys=[],dic=config) ports_result = extracted_values[0] diff --git a/src/device/service/drivers/oc_driver/templates/Tools.py b/src/device/service/drivers/oc_driver/templates/Tools.py index 1bd217b6cf9bf402cb5d3455d8bb820246663919..b4b2249c0c1ea961ca0e365c5f0a06d824215c62 100644 --- a/src/device/service/drivers/oc_driver/templates/Tools.py +++ b/src/device/service/drivers/oc_driver/templates/Tools.py @@ -367,4 +367,100 @@ def roadm_values_extractor (data_xml:str,resource_keys:list,dic:dict): resource_value = {'uuid': port, 'type':'MG_ON_OPTICAL_PORT_WAVEBAND'} ports_result.append((resource_key, resource_value)) - return [ports_result] \ No newline at end of file + return [ports_result] + + + + #/////////////// 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): + circuit_info ={} + for circuit in circuits: + + 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) + + circuit_ports=[] + 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 + + + + \ No newline at end of file diff --git a/src/device/service/drivers/oc_driver/templates/descovery_tool/roadms.py b/src/device/service/drivers/oc_driver/templates/descovery_tool/roadms.py new file mode 100644 index 0000000000000000000000000000000000000000..499a85e9941b572f02c2f5659416cf897c48f73d --- /dev/null +++ b/src/device/service/drivers/oc_driver/templates/descovery_tool/roadms.py @@ -0,0 +1,199 @@ + +# 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 + + + + + + + + + + + +######################################################################### + +#################################### ROADMAs ############################ + +########################################################################## + +def extract_roadm_ports (xml_data:str): + + ports =[] + pattern = r'\bMG_ON_OPTICAL_PORT_WAVEBAND\b' + 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://openconfig.net/yang/platform'} + ports = [] + components = root.findall('.//oc:component',namespace) + print(f"component {components}") + + + for component in components: + + properties = component.find(".//oc:properties",namespace) + + if (properties is not None): + for property in properties : + value = property.find(".//oc:value",namespace) + + if (re.search(pattern,value.text)): + name_element= component.find(".//oc:name",namespace) + ports.append(name_element.text) + return ports + + + + +def roadm_values_extractor (data_xml:str,resource_keys:list,dic:dict): + ports_result=[] + ports = extract_roadm_ports(data_xml) + + if len(ports)>0 : + for port in ports : + + resource_key = '/endpoints/endpoint[{:s}]'.format(port) + resource_value = {'uuid': port, 'type':'MG_ON_OPTICAL_PORT_WAVEBAND'} + ports_result.append((resource_key, resource_value)) + + return [ports_result] + + + + #/////////////// 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 + 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 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] + + + + + + + \ No newline at end of file diff --git a/src/device/service/drivers/oc_driver/templates/descovery_tool/transponders.py b/src/device/service/drivers/oc_driver/templates/descovery_tool/transponders.py new file mode 100644 index 0000000000000000000000000000000000000000..fdba2649c62a97989ffa7c6af870abca2e6f610f --- /dev/null +++ b/src/device/service/drivers/oc_driver/templates/descovery_tool/transponders.py @@ -0,0 +1,301 @@ +# 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 + + + +def add_value_from_tag(target : Dict, field_name: str, field_value : ET.Element, cast=None) -> None: + if isinstance(field_value,str) or field_value is None or field_value.text is None: return + field_value = field_value.text + if cast is not None: field_value = cast(field_value) + target[field_name] = field_value + +def add_value_from_collection(target : Dict, field_name: str, field_value : Collection) -> None: + if field_value is None or len(field_value) == 0: return + target[field_name] = field_value + + +def generate_templates(resource_key: str, resource_value: str, channel:str) -> str: # template management to be configured + + result_templates = [] + data={} + data['name']=channel + data['resource_key']=resource_key + data['value']=resource_value + #result_templates.append(create_physical_config(data)) + + return result_templates + + +def extract_status (dic:dict,resource_key:str,xml_data:str,channel_name:str): + + xml_bytes = xml_data.encode("utf-8") + root = ET.fromstring(xml_bytes) + channel_name=channel_name if 'index' not in channel_name else channel_name['index'] + index=None + if channel_name.find('-') != -1 : + index= channel_name.split("-")[1] + + + namespaces = { "td": "http://openconfig.net/yang/terminal-device"} + channels = root.findall(f".//td:terminal-device/td:logical-channels/td:channel",namespaces) + for channel in channels : + + index_ele= channel.find(f".//td:config[td:index='{index}']/td:{resource_key}",namespaces) + if index_ele is not None : + dic["status"]=index_ele.text + print(index_ele.text) + return dic + + +def extract_channel_xmlns (data_xml:str,is_opticalband:bool): + xml_bytes = data_xml.encode("utf-8") + root = ET.fromstring(xml_bytes) + + namespace=None + channels=None + + if (not is_opticalband) : + + optical_channel_namespaces = { + 'ns': 'urn:ietf:params:xml:ns:netconf:base:1.0', + 'oc': 'http://openconfig.net/yang/platform', + } + + channels= root.find('.//{*}optical-channel',optical_channel_namespaces) + if channels is not None : + optical_channel_namespace = channels.tag.replace("optical-channel", "") + namespace=optical_channel_namespace.replace("{", "").replace("}", "") + else : + optical_band_namespaces= { + 'oc':'http://openconfig.net/yang/wavelength-router' + } + + channels= root.find('.//{*}optical-bands',optical_band_namespaces) + if channels is not None: + optical_channel_namespace = channels.tag.replace("optical-bands", "") + namespace=optical_channel_namespace.replace("{", "").replace("}", "") + + + return namespace + +def extract_channels_based_on_channelnamespace (xml_data:str,channel_namespace:str,is_opticalband:bool): + xml_bytes = xml_data.encode("utf-8") + root = ET.fromstring(xml_bytes) + channels=[] + + # Find the component names whose children include the "optical-channel" element + if (not is_opticalband): + namespace = {'namespace': 'http://openconfig.net/yang/platform','cn':channel_namespace} + + component_names = root.findall('.//namespace:component[cn:optical-channel]',namespace) + + # Extract and print the component names + for component in component_names: + component_name = component.find('namespace:name', namespace).text + channels.append({"index":component_name}) + else : + namespaces = { + 'wr': 'http://openconfig.net/yang/wavelength-router', + 'fs': channel_namespace + } + + wl = root.findall('.//fs:optical-band',namespaces=namespaces) + + for component in wl : + index=component.find('.//fs:index',namespaces).text + dest_port_name = component.find('.//fs:dest/fs:config/fs:port-name', namespaces).text + + # Retrieve port-name for source (assuming it exists in the XML structure) + source_port_name = component.find('.//fs:source/fs:config/fs:port-name', namespaces).text + channels.append({"index":index,"endpoints":(source_port_name,dest_port_name)}) + + # Retrieve port-name for dest + + return channels + +def extract_channels_based_on_type (xml_data:str): + xml_bytes = xml_data.encode("utf-8") + root = ET.fromstring(xml_bytes) + + namespace = {'oc': 'http://openconfig.net/yang/platform', 'typex': 'http://openconfig.net/yang/platform-types'} + channel_names = [] + components = root.findall('.//oc:component', namespace) + for component in components: + + type_element = component.find('.//oc:state/oc:type[.="oc-opt-types:OPTICAL_CHANNEL"]',namespaces=namespace) + + if type_element is not None and type_element.text == 'oc-opt-types:OPTICAL_CHANNEL': + name_element = component.find('oc:name', namespace) + if name_element is not None: + channel_names.append(name_element.text) + return channel_names + +def extract_value(resource_key:str,xml_data:str,dic:dict,channel_name:str,channel_namespace:str): + logging.info(f"resource_key {resource_key} and channgel_name {channel_name} and channel_namespace {channel_namespace}") + xml_bytes = xml_data.encode("utf-8") + root = ET.fromstring(xml_bytes) + channel_name=channel_name if 'index' not in channel_name else channel_name['index'] + namespace = {'oc': 'http://openconfig.net/yang/platform', + 'td': channel_namespace} + + element = root.find(f'.//oc:component[oc:name="{channel_name}"]', namespace) + + if element is not None: + parameter= element.find(f'.//td:{resource_key}',namespace) + if (parameter is not None): + value = parameter.text + dic[resource_key]=value + else : + logging.info("parameter is None") + + else: + logging.info("element is None") + print(" element not found.") + logging.info(f"dic {dic}") + return dic + + +def extract_port_value (xml_string:list,port_name:str): + + xml_bytes = xml_string.encode("utf-8") + root = ET.fromstring(xml_bytes) + + namespace = {"oc": "http://openconfig.net/yang/platform"} + component=root.find(f".//oc:component[oc:name='{port_name}']", namespace) + onos_index = component.find( + f".//oc:property//oc:state/oc:name[.='onos-index']/../oc:value", namespace + ).text + + return (port_name,onos_index) + + + + +def extract_tranceiver (data_xml:str,dic:dict): + xml_bytes = data_xml.encode("utf-8") + root = ET.fromstring(xml_bytes) + namespaces = { + 'ns': 'urn:ietf:params:xml:ns:netconf:base:1.0', + 'oc': 'http://openconfig.net/yang/platform', + 'oc-terminal': 'http://openconfig.net/yang/terminal-device', + 'oc-platform-types': 'http://openconfig.net/yang/platform-types' + } + + + transceiver_components = root.findall('.//oc:component/oc:state/[oc:type="oc-platform-types:TRANSCEIVER"]../oc:state/oc:name', namespaces) + + component_names = [component.text for component in transceiver_components] + dic['transceiver']=component_names + return dic + +def extract_interface (xml_data:str,dic:dict): + xml_bytes = xml_data.encode("utf-8") + root = ET.fromstring(xml_bytes) + namespaces = { + 'ns': 'urn:ietf:params:xml:ns:netconf:base:1.0', + 'oc': 'http://openconfig.net/yang/interfaces', + } + ip_namespaces = { + 'oc': 'http://openconfig.net/yang/interfaces', + 'ip': 'http://openconfig.net/yang/interfaces/ip', + } + + interfaces = root.findall('.//oc:interfaces/oc:interface', namespaces) + interface_names = [interface.find('oc:name', namespaces).text for interface in interfaces] + interface_enabled=[interface.find('oc:config/oc:enabled', namespaces).text for interface in interfaces] + ip_address_element = root.find('.//ip:ip', ip_namespaces) + interface_prefix_length=root.find('.//ip:prefix-length',ip_namespaces) + if (len(interface_names) > 0): + dic['interface']={"name":interface_names[0],'ip':ip_address_element.text,'enabled':interface_enabled[0],"prefix-length":interface_prefix_length.text} + else : + dic['interface']={} + return dic + +def has_opticalbands(xml_data:str): + xml_bytes = xml_data.encode("utf-8") + root = ET.fromstring(xml_bytes) + + has_opticalbands=False + elements= root.find('.//{*}optical-bands') + + if (elements is not None and len(elements) >0): + has_opticalbands=True + else : + has_opticalbands=False + return has_opticalbands + +def extract_ports_based_on_type (xml_data:str): + pattern = r':\s*PORT\b' + xml_bytes = xml_data.encode("utf-8") + root = ET.fromstring(xml_bytes) + namespace = {'oc': 'http://openconfig.net/yang/platform', 'typex': 'http://openconfig.net/yang/platform-types'} + ports = [] + components = root.findall(".//oc:state[oc:type]",namespace) + for component in components: + type_ele = component.find(".//oc:type",namespace) + match = re.search(pattern, type_ele.text) + if match is not None : + name_element= component.find(".//oc:name",namespace) + port_name =name_element.text + port_index=name_element.text.split("-")[1] + port = (port_name,port_index) + ports.append(port) + return ports + +def transponder_values_extractor(data_xml:str,resource_keys:list,dic:dict): + + endpoints=[] + is_opticalband=has_opticalbands(xml_data=data_xml) + channel_namespace=extract_channel_xmlns(data_xml=data_xml,is_opticalband=is_opticalband) + # channel_names=extract_channels_based_on_type(xml_data=data_xml) + # if len(channel_names)==0 : + channel_names= extract_channels_based_on_channelnamespace(xml_data=data_xml,channel_namespace=channel_namespace,is_opticalband=is_opticalband) + ports = extract_ports_based_on_type(data_xml) + optical_channels_params=[] + ports_result=[] + if (is_opticalband): + endpoints=channel_names + else: + + for channel_name in channel_names: + dic={} + for resource_key in resource_keys : + + if (resource_key != 'admin-state'): + + dic=extract_value(dic=dic,resource_key=resource_key,xml_data=data_xml + ,channel_name=channel_name,channel_namespace=channel_namespace) + else : + dic = extract_status(dic=dic,resource_key=resource_key,xml_data=data_xml, channel_name=channel_name) + dic["name"]=channel_name + endpoints.append({"endpoint_uuid":{"uuid":channel_name}}) + optical_channels_params.append(dic) + #transceivers_dic=extract_tranceiver(data_xml=data_xml,dic={}) + transceivers_dic={"transceiver":[]} + #interfaces_dic=extract_interface(xml_data=data_xml,dic={}) + if len(ports)>0 : + for port in ports : + endpoint_name,endpoint_id=port + resource_key = '/endpoints/endpoint[{:s}]'.format(endpoint_id) + resource_value = {'uuid': endpoint_id, 'type':endpoint_name} + ports_result.append((resource_key, resource_value)) + + + return [transceivers_dic,optical_channels_params,channel_namespace,endpoints,ports_result] + \ No newline at end of file diff --git a/src/webui/service/opticalconfig/routes.py b/src/webui/service/opticalconfig/routes.py index 1bc3ddc1d830772ae0e157ed164c66e0eebf34a0..7272a2f60cc620e03bf90e0dcb542cffb3d39942 100644 --- a/src/webui/service/opticalconfig/routes.py +++ b/src/webui/service/opticalconfig/routes.py @@ -40,7 +40,9 @@ def home() : for configs in opticalConfig_list.opticalconfigs: value=json.loads(configs.config) if type(configs.config)==str else configs.config - value["channels_number"]=len(value['channels']) + config_type = value["type"] + if (config_type == 'optical-transponder'): + value["channels_number"]=len(value['transponder']['channels']) # value['operationalMode']=value['operational-mode'] # value['targetOutputPower']=value['target-output-power'] @@ -63,7 +65,7 @@ def show_details(config_uuid): opticalconfigId=OpticalConfigId() opticalconfigId.opticalconfig_uuid=config_uuid device_details=[] - interfaces=[] + context_client.connect() response = context_client.SelectOpticalConfig(opticalconfigId) context_client.close() @@ -71,24 +73,30 @@ def show_details(config_uuid): LOGGER.info("response from show detail %s",response) opticalConfig = OpticalConfig() opticalConfig.CopyFrom(response) - - config =json.loads(opticalConfig.config) - LOGGER.info("config details from show detail %s",config) - interfaces=config["interfaces"] - + device_name="" + config =json.loads(opticalConfig.config) if ("device_name" in config): device_name= config["device_name"] - 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 "" + config_type = config["type"] - device_details.append(new_config) + if config_type == 'optical-transponder': + + LOGGER.info("config details from show detail %s",config) + + + + + for channel in config['transponder']['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) LOGGER.info("device details %s",device_details) return render_template('opticalconfig/details.html', device=device_details,config_id=config_uuid,device_name=device_name) @@ -143,7 +151,8 @@ def update_externally () : new_opticalconfig = OpticalConfig() new_opticalconfig.CopyFrom(opticalconfig) config =json.loads(opticalconfig.config) - target_channel =next((item for item in config['channels'] if item["name"]['index'] == channel_name) , None) + channels= config['transponder']['channels'] + target_channel =next((item for item in channels if item["name"]['index'] == channel_name) , None) LOGGER.info(f"target channel {target_channel}") target_power=device.get( "target-output-power") freq = device.get("frequency") diff --git a/test.py b/test.py index 970d4bc061224662b870220f499942711b90bb58..770ca513b3adab10637e1514e69cf2c72013b113 100644 --- a/test.py +++ b/test.py @@ -7,69 +7,95 @@ from uuid import UUID, uuid4, uuid5 import logging +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_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) + -edit_config_t = ''' - <config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> - <terminal-device xmlns="http://openconfig.net/yang/terminal-device"> - <logical-channels> - <channel> - <index>1</index> - <config> - <admin-state>DISABLED</admin-state> - </config> - </channel> - </logical-channels> - </terminal-device> - </config> -''' - -edit_config_r_media_channel = ''' - <config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> - <wavelength-router xmlns="http://openconfig.net/yang/wavelength-router"> - <media-channels > - <channel operation="delete" > - <index>1</index> - <config> - <index>1</index> - </config> - </channel> - </media-channels> - </wavelength-router> - </config> -''' -edit_config_r_optical_band = ''' -<config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> - <wavelength-router xmlns="http://openconfig.net/yang/wavelength-router"> - <optical-bands xmlns="http://flex-scale-project.eu/yang/flex-scale-mg-on" > - <optical-band > - <index>1</index> - <config> - <index>1</index> - <admin-status>DISABLED</admin-status> - </config> + namespace = {'oc': "http://org/openroadm/device"} + + circuits = root.findall('.//oc:circuit-packs',namespace) + + circuits_list =[] + # print(f"component {components}") + + if (circuits is not None): + circuit_info ={} + for circuit in circuits: + + 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) + + circuit_ports=[] + 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 - </optical-band> - </optical-bands> - </wavelength-router> -</config> -''' -delete_optical_band = ''' -<config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> - <wavelength-router xmlns="http://openconfig.net/yang/wavelength-router"> - <optical-bands xmlns="http://flex-scale-project.eu/yang/flex-scale-mg-on" > - <optical-band operation ="delete"> - <index>1</index> - <config> - <index>1</index> - - </config> - </optical-band> - </optical-bands> - </wavelength-router> -</config> -''' @@ -78,62 +104,32 @@ delete_optical_band = ''' device = { - 'host': '10.0.2.4', # IP address or hostname of the remote machine - 'port': 2026, # SSH port (default: 22) - 'username': 'admin', # SSH username - 'password': 'admin', # SSH password + 'host': '172.17.0.2', # IP address or hostname of the remote machine + 'port': 830, # SSH port (default: 22) + 'username': 'openroadm', # SSH username + 'password': 'openroadm', # SSH password 'device_params': {'name': 'default'}, 'hostkey_verify':False, "allow_agent":False ,"look_for_keys":False } -def extract_status (xml_data:str,index:int): - xml_bytes = xml_data.encode("utf-8") - root = ET.fromstring(xml_bytes) - namespaces = { "td": "http://openconfig.net/yang/terminal-device"} - channels = root.findall(f".//td:terminal-device/td:logical-channels/td:channel",namespaces) - for channel in channels : - - index_ele= channel.find(f".//td:config[td:index='{index}']/td:admin-state",namespaces) - if index_ele is not None : - - print(index_ele.text) - - + if __name__ == '__main__': - # with manager.connect(host=device['host'] - # ,port=device['port'] - # ,username=device['username'] - # ,password=device['password'] - # ,hostkey_verify=device['hostkey_verify'] - # ,allow_agent=device['allow_agent'] - # ,look_for_keys=device['look_for_keys']) as m : + with manager.connect(host=device['host'] + ,port=device['port'] + ,username=device['username'] + ,password=device['password'] + ,hostkey_verify=device['hostkey_verify'] + ,allow_agent=device['allow_agent'] + ,look_for_keys=device['look_for_keys']) as m : - # result = m.get_config (source="running").data_xml - # with open("context.log","w") as f: - # f.write(result) - bin_num ='0b00001111111111111111' - bin_num1='0b00001111111111111111' - int_num = int(bin_num1,2) - print (int_num) - slot= dict() - bin_num1 = bin(int_num) - - print (bin_num1) - sliced_num=bin_num1[2:] - if (len(sliced_num) != 20) : - for i in range(0,20 - len(sliced_num)): - sliced_num='0'+sliced_num - print (sliced_num) - print(len(sliced_num)) - for i in range(len(sliced_num)): - slot[str(i+1)]=int(sliced_num[i]) - print(slot) - - - - - \ No newline at end of file + result = m.get_config (source="running").data_xml + road_info= extract_openroadm_info(result) + circuits=extract_roadm_circuits_pack(result) + print(f"roadm_info {road_info}") + #print(f"circuits {circuits}") + # with open("context.log","w") as f: + # print (result,file=f)