diff --git a/manifests/contextservice.yaml b/manifests/contextservice.yaml index 568c3a51dfe0b6908c0055ef2d44c2d183e166a8..3e8fcfbb5ba1884b901eda264b62d945848d56a7 100644 --- a/manifests/contextservice.yaml +++ b/manifests/contextservice.yaml @@ -40,7 +40,7 @@ spec: - name: MB_BACKEND value: "nats" - name: LOG_LEVEL - value: "INFO" + value: "DEBUG" - name: ALLOW_EXPLICIT_ADD_DEVICE_TO_TOPOLOGY value: "FALSE" - name: ALLOW_EXPLICIT_ADD_LINK_TO_TOPOLOGY diff --git a/manifests/serviceservice.yaml b/manifests/serviceservice.yaml index aa94e4269daae85872573d2dc32a5d56da89673b..72c3015b31844f7bd38e47f7be2ed2691db59adb 100644 --- a/manifests/serviceservice.yaml +++ b/manifests/serviceservice.yaml @@ -36,7 +36,7 @@ spec: - containerPort: 9192 env: - name: LOG_LEVEL - value: "INFO" + value: "DEBUG" readinessProbe: exec: command: ["/bin/grpc_health_probe", "-addr=:3030"] diff --git a/manifests/webuiservice.yaml b/manifests/webuiservice.yaml index e28b2bb64adb6b92e2e26a07c8a961460f49d2a3..20d7c28ab498224f9bf8879fce625c98c61cbc34 100644 --- a/manifests/webuiservice.yaml +++ b/manifests/webuiservice.yaml @@ -128,7 +128,7 @@ spec: kind: Deployment name: webuiservice minReplicas: 1 - maxReplicas: 20 + maxReplicas: 1 metrics: - type: Resource resource: diff --git a/proto/context.proto b/proto/context.proto index 685c757937d82cafe662e383d8cf30cbb677930b..ea8888c56c8ebabac6e4e2a2ed64a5c338f54684 100644 --- a/proto/context.proto +++ b/proto/context.proto @@ -90,6 +90,10 @@ service ContextService { rpc DeleteOpticalLink (LinkId ) returns (Empty ) {} rpc GetOpticalLinkList (Empty ) returns (OpticalLinkList ) {} + rpc GetOpticalBand (Empty ) returns (OpticalBandList) {} + rpc SelectOpticalBand (OpticalBandId ) returns (OpticalBand) {} + + rpc DeleteServiceConfigRule(ServiceConfigRule) returns (Empty ) {} } @@ -704,6 +708,7 @@ message OpticalLinkList { message OpticalLinkDetails { + float length = 1; string src_port = 2; string dst_port = 3; @@ -713,6 +718,7 @@ message OpticalLinkDetails { map<string, int32> c_slots = 7; map<string, int32> l_slots = 8; map<string, int32> s_slots = 9; + } message OpticalLink { @@ -738,6 +744,11 @@ message OpticalBand { ConnectionId connection_id =2 ; ChannelId channel_id = 3; ServiceId service_id =4; + oneof field { + Service service =5 ; + Connection connection =6 ; + string channel = 7; + } } diff --git a/src/common/tools/context_queries/Device.py b/src/common/tools/context_queries/Device.py index cdb1af073fdae940b676720eb58eabc8594c86e8..495e832b04a3730bcbf0ee9b5ffac054570b7055 100644 --- a/src/common/tools/context_queries/Device.py +++ b/src/common/tools/context_queries/Device.py @@ -30,19 +30,21 @@ def get_device( device_filter.include_endpoints = include_endpoints device_filter.include_config_rules = include_config_rules device_filter.include_components = include_components - + try: ro_devices = context_client.SelectDevice(device_filter) + if len(ro_devices.devices) == 0: return None assert len(ro_devices.devices) == 1 ro_device = ro_devices.devices[0] if not rw_copy: return ro_device rw_device = Device() rw_device.CopyFrom(ro_device) + return rw_device except grpc.RpcError as e: + # LOGGER.exception('Unable to get Device({:s})'.format(str(device_uuid))) if e.code() != grpc.StatusCode.NOT_FOUND: raise # pylint: disable=no-member - #LOGGER.exception('Unable to get Device({:s})'.format(str(device_uuid))) return None def get_existing_device_uuids(context_client : ContextClient) -> Set[str]: diff --git a/src/common/tools/context_queries/OpticalConfig.py b/src/common/tools/context_queries/OpticalConfig.py index 00ccfa8a9046273d77f8b1ac628a231234420204..c7408e060cda4560c0b1a56bc71cba52ddf81c14 100644 --- a/src/common/tools/context_queries/OpticalConfig.py +++ b/src/common/tools/context_queries/OpticalConfig.py @@ -14,9 +14,11 @@ from common.method_wrappers.ServiceExceptions import InvalidArgumentsException +from context.client.ContextClient import ContextClient +import logging from typing import Optional, Union from uuid import UUID, uuid4, uuid5 - +from common.proto.context_pb2 import OpticalBand, OpticalBandId, Empty # Generate a UUIDv5-like from the SHA-1 of "TFS" and no namespace to be used as the NAMESPACE for all # the context UUIDs generated. For efficiency purposes, the UUID is hardcoded; however, it is produced # using the following code: @@ -64,3 +66,34 @@ def opticalconfig_get_uuid( raise InvalidArgumentsException([ ('name', device_name), ], extra_details=['At least one is required to produce a OpticalConfig UUID']) + + +def ob_get_uuid( + ob_name:str , allow_random : bool = False +) -> str: + + if ( ob_name is not None): + + + result = get_uuid_from_string(f'ob_{ob_name}') + + return result + if allow_random: return get_uuid_random() + + raise InvalidArgumentsException([ + ('ob_name ', ob_name), + ], extra_details=['ob_name is required to produce a Optical band UUID']) + + + +def find_optical_band (ob_index)->OpticalBand: + + op_uuid = ob_get_uuid(ob_index) + op_id=OpticalBandId() + op_id.opticalband_uuid.uuid =op_uuid + try: + ctxt = ContextClient() + target_ob= ctxt.SelectOpticalBand(op_id) + return target_ob + except Exception as e : + logging.debug(f"error in finding optical band {e}") \ No newline at end of file diff --git a/src/common/tools/object_factory/OpticalLink.py b/src/common/tools/object_factory/OpticalLink.py index f878f22f26c8ded2c9e776b3cc21cf0311d63bed..e4be5770a1d349acb35e28f54a7bb41c39a9dda0 100644 --- a/src/common/tools/object_factory/OpticalLink.py +++ b/src/common/tools/object_factory/OpticalLink.py @@ -38,3 +38,50 @@ def correct_slot(dic: dict) -> dict: _dict[key]=1 #print(f"result {_dict}") return _dict + + +## To be deleted , needed now for development purpose ## + +def order_list (lst:list[tuple])->list: + + if (len(lst)<=1): + return lst + else : + pivot,bit_val =lst[0] + lst_smaller = [] + lst_greater = [] + for element in lst[1:]: + key,val=element + if (key <= pivot): + lst_smaller.append(element) + else : + lst_greater.append(element) + return order_list(lst_smaller) + [(pivot,bit_val)] + order_list(lst_greater) + + +def list_to_dict (lst:list[tuple[int,int]])->dict: + dct = dict() + for ele in lst : + key,value = ele + dct[str(key)]=value + return dct + + +def order_dict (dct:dict)->dict: + + lst = list() + for key,value in sorted(dct.items()): + lst.append((int(key),value)) + ordered_lst= order_list(lst) + if (len(ordered_lst)>0): + return list_to_dict (ordered_lst) + +def order_dict_v1 (dct:dict)->dict: + + lst = list() + for key,value in dct.items(): + lst.append((int(key),value)) + ordered_lst= order_list(lst) + if (len(ordered_lst)>0): + return list_to_dict (ordered_lst) + \ No newline at end of file diff --git a/src/context/client/ContextClient.py b/src/context/client/ContextClient.py index d3513c36a0f79a82b92669b42eb8fffd53e69c52..425f14f3a69dfa97a33a1c248bde959f5500f119 100644 --- a/src/context/client/ContextClient.py +++ b/src/context/client/ContextClient.py @@ -492,6 +492,13 @@ class ContextClient: response = self.stub.GetOpticalBand(request) LOGGER.debug('GetOpticalBand result: {:s}'.format(grpc_message_to_json_string(response))) return response + + @RETRY_DECORATOR + def SelectOpticalBand(self, request : OpticalBandId) -> OpticalBand: + LOGGER.debug('SelectOpticalBand request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.SelectOpticalBand(request) + LOGGER.debug('SelectOpticalBand result: {:s}'.format(grpc_message_to_json_string(response))) + return response @RETRY_DECORATOR def SetOpticalBand(self,request : OpticalBand) -> Empty: diff --git a/src/context/service/ContextServiceServicerImpl.py b/src/context/service/ContextServiceServicerImpl.py index d789b7863a4b4fc0635008a3a5d46eb7f88be03f..45d741393be3e1fa887ee9a690a335d816165fed 100644 --- a/src/context/service/ContextServiceServicerImpl.py +++ b/src/context/service/ContextServiceServicerImpl.py @@ -70,7 +70,7 @@ from .database.OpticalLink import ( ) from .database.ConfigRule import delete_config_rule from .database.OpticalBand import ( - get_optical_band,set_optical_band + get_optical_band,set_optical_band , select_optical_band ) LOGGER = logging.getLogger(__name__) @@ -366,6 +366,11 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer def GetOpticalBand(self, request : Empty, context : grpc.ServicerContext) -> OpticalBandList: result = get_optical_band(self.db_engine) return OpticalBandList(opticalbands=result) + + safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def SelectOpticalBand(self, request : OpticalBandId, context : grpc.ServicerContext) -> OpticalBand: + result = select_optical_band(self.db_engine,request ) + return result @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def SetOpticalBand(self, request : OpticalBand, context : grpc.ServicerContext) -> Empty: diff --git a/src/context/service/database/OpticalBand.py b/src/context/service/database/OpticalBand.py index 75c4e029c7e9f4405bc9e1d332bbfbb515c3ce91..08b88d55b1e8023b2c82b9b2e933bd0a231859b8 100644 --- a/src/context/service/database/OpticalBand.py +++ b/src/context/service/database/OpticalBand.py @@ -17,7 +17,7 @@ from sqlalchemy.engine import Engine from sqlalchemy.orm import Session, selectinload, sessionmaker from sqlalchemy_cockroachdb import run_transaction from sqlalchemy.dialects.postgresql import insert - +from common.method_wrappers.ServiceExceptions import NotFoundException from typing import Dict, List from common.proto.context_pb2 import OpticalBand,OpticalBandId,OpticalBandList from .models.OpticalConfig.OpticalBandModel import OpticalBandModel @@ -31,11 +31,27 @@ def get_optical_band(db_engine : Engine): def callback(session:Session): results = session.query(OpticalBandModel).all() - return {"opticalbands":[obj.dump_id() for obj in results]} + return [obj.dump() for obj in results] obj = run_transaction(sessionmaker(bind=db_engine), callback) return obj +def select_optical_band( db_engine : Engine ,request:OpticalBandId): + ob_uuid = request.opticalband_uuid.uuid + def callback(session : Session) -> OpticalBand: + stmt = session.query(OpticalBandModel) + stmt = stmt.filter_by(ob_uuid=ob_uuid) + obj = stmt.first() + if obj is not None: + + return obj.dump() + return None + result= run_transaction(sessionmaker(bind=db_engine, expire_on_commit=False), callback) + if result is None : + return result + return OpticalBand(**result) + + def set_optical_band(db_engine : Engine, ob_data ): def callback(session : Session) -> List[Dict]: diff --git a/src/context/service/database/OpticalConfig.py b/src/context/service/database/OpticalConfig.py index 4a8ab52a2ce0de73ef93944ecdbe037b65946f8d..fe47df193cec2907e3ac302aefe466532c3fc745 100644 --- a/src/context/service/database/OpticalConfig.py +++ b/src/context/service/database/OpticalConfig.py @@ -22,11 +22,14 @@ from sqlalchemy.orm import Session, sessionmaker from sqlalchemy_cockroachdb import run_transaction from common.proto.context_pb2 import OpticalConfig, OpticalConfigId, Empty, EventTypeEnum from .models.OpticalConfig.OpticalConfigModel import OpticalConfigModel -from .models.OpticalConfig.TransponderModel import TransponderTypeModel, OpticalChannelModel +from .models.OpticalConfig.TransponderModel import TransponderTypeModel, OpticalChannelModel, TransponderInterfaceModel from .models.OpticalConfig.RoadmModel import RoadmTypeModel, ChannelModel, ORInterfaceModel + + from context.service.database.uuids.OpticalConfig import ( - channel_get_uuid , opticalconfig_get_uuid ,transponder_get_uuid,roadm_get_uuid, + channel_get_uuid , opticalconfig_get_uuid ,transponder_get_uuid,roadm_get_uuid, interface_get_uuid , ob_get_uuid + ) from .Events import notify_event_opticalconfig from .OpticalBand import set_optical_band @@ -74,8 +77,35 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig): if channel_namespace is None and 'channel_namespace' in config: channel_namespace=config['channel_namespace'] - if 'transceivers' in config and len(config['transceivers']['transceiver']) > 0: - transceivers = [transceiver for transceiver in config ['transceivers']['transceiver']] + # if 'transceivers' in config and len(config['transceivers']['transceiver']) > 0: + # transceivers = [transceiver for transceiver in config ['transceivers']['transceiver']] + if 'interfaces' in config and len(config['interfaces']) > 0: + for interface in config['interfaces']: + interface_name=interface["name"] if "name" in interface else None + interfaces.append({ + "transponder_uuid" : transponder_get_uuid(device_id), + 'interface_uuid':interface_get_uuid(interface_name,device_uuid), + "name" :interface_name, + "status":interface["status"] if "status" in interface else None, + "operation_status":interface["operation_status"] if "operation_status" in interface else None, + "ifindex":interface["ifindex"] if "ifindex" in interface else None, + "in_octets":interface["in_octets"] if "in_octets" in interface else None, + "in_pkts":interface["in_pkts"] if "in_pkts" in interface else None, + "in_unicast_pkts":interface["in_unicast_pkts"] if "in_unicast_pkts" in interface else None, + "in_broadcast_pkts":interface["in_broadcast_pkts"] if "in_broadcast_pkts" in interface else None, + "in_multicast_pkts":interface["in_multicast_pkts"] if "in_multicast_pkts" in interface else None, + "out_discards":interface["out_discards"] if "out_discards" in interface else None, + "out_errors":interface["out_errors"] if "out_errors" in interface else None, + "in_discards":interface["in_discards"] if "in_discards" in interface else None, + "in_errors":interface["in_errors"] if "in_errors" in interface else None, + "out_octets":interface["out_octets"] if "out_octets" in interface else None, + "out_pkts":interface["out_pkts"] if "out_pkts" in interface else None, + "out_unicast_pkts":interface["out_unicast_pkts"] if "out_unicast_pkts" in interface else None, + "out_broadcast_pkts":interface["out_broadcast_pkts"] if "out_broadcast_pkts" in interface else None, + "out_multicast_pkts":interface["out_multicast_pkts"] if "out_multicast_pkts" in interface else None, + "last_clear":interface["last_clear"] if "last_clear" in interface else None + }) + if 'channels' in config and len(config['channels']) > 0: #channels = [channel['name']['index'] for channel in config['channels']] for channel_params in config['channels']: @@ -93,7 +123,7 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig): transponder.append({ "transponder_uuid" : transponder_get_uuid(device_id), "transcievers" : transceivers, - "interfaces" : None, + "opticalconfig_uuid": opticalconfig_uuid, }) @@ -212,7 +242,19 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig): ) ) stmt = stmt.returning(OpticalChannelModel.channel_uuid) - opticalChannel_id = session.execute(stmt).fetchone() + channel_id = session.execute(stmt).fetchone() + if (len(interfaces)>0) : + model_columns = inspect(TransponderInterfaceModel).c.keys() + stmt = insert(TransponderInterfaceModel).values(interfaces) + + stmt = stmt.on_conflict_do_update( + index_elements=[TransponderInterfaceModel.interface_uuid ], + set_={field: stmt.excluded[field] for field in model_columns if field != 'interface_uuid' } + + ) + stmt = stmt.returning(TransponderInterfaceModel.interface_uuid) + interface_id = session.execute(stmt).fetchone() + if config_type == DeviceTypeEnum.OPTICAL_ROADM._value_: if len(roadms) > 0: @@ -289,6 +331,7 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): OpticalConfig_data = [] config_type = None optical_bands = [] + interfaces=[] #is_transpondre = False opticalconfig_uuid = opticalconfig_get_uuid(device_id) is_optical_band=None @@ -313,6 +356,33 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): if 'transceivers' in config['new_config'] and len(config['new_config']['transceivers']['transceiver']) > 0: transceivers = [transceiver for transceiver in config['new_config'] ['transceivers']['transceiver']] + if 'interfaces' in config['new_config'] and len(config['new_config']['interfaces']) > 0: + for interface in config['new_config']['interfaces']: + interface_name=interface["name"] if "name" in interface else None + interfaces.append({ + "transponder_uuid" : transponder_get_uuid(device_id), + 'interface_uuid':interface_get_uuid(interface_name,device_uuid), + "name" :interface_name, + "status":interface["status"] if "status" in interface else None, + "operation_status":interface["operation_status"] if "operation_status" in interface else None, + "ifindex":interface["ifindex"] if "ifindex" in interface else None, + "in_octets":interface["in_octets"] if "in_octets" in interface else None, + "in_pkts":interface["in_pkts"] if "in_pkts" in interface else None, + "in_unicast_pkts":interface["in_unicast_pkts"] if "in_unicast_pkts" in interface else None, + "in_broadcast_pkts":interface["in_broadcast_pkts"] if "in_broadcast_pkts" in interface else None, + "in_multicast_pkts":interface["in_multicast_pkts"] if "in_multicast_pkts" in interface else None, + "out_discards":interface["out_discards"] if "out_discards" in interface else None, + "out_errors":interface["out_errors"] if "out_errors" in interface else None, + "in_discards":interface["in_discards"] if "in_discards" in interface else None, + "in_errors":interface["in_errors"] if "in_errors" in interface else None, + "out_octets":interface["out_octets"] if "out_octets" in interface else None, + "out_pkts":interface["out_pkts"] if "out_pkts" in interface else None, + "out_unicast_pkts":interface["out_unicast_pkts"] if "out_unicast_pkts" in interface else None, + "out_broadcast_pkts":interface["out_broadcast_pkts"] if "out_broadcast_pkts" in interface else None, + "out_multicast_pkts":interface["out_multicast_pkts"] if "out_multicast_pkts" in interface else None, + "last_clear":interface["last_clear"] if "last_clear" in interface else None + }) + if 'channels' in config['new_config'] and len(config['new_config']['channels']) > 0: #channels = [channel['name']['index'] for channel in config['channels']] @@ -354,7 +424,7 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): transponder.append({ "transponder_uuid" : transponder_get_uuid(device_id), "transcievers" : transceivers, - "interfaces" : None, + "opticalconfig_uuid": opticalconfig_uuid, }) @@ -363,6 +433,7 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): channel_namespace=config['new_config']['channel_namespace'] if 'is_opticalband' in config and not config['is_opticalband']: is_optical_band=config['is_opticalband'] + bidir = config['new_config']['bidir'] #channels = [channel['name']['index'] for channel in config['channels']] if 'flow_handled' in config and len(config['flow_handled'])>0 : num = 0 @@ -385,6 +456,7 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): "optical_band_parent" : str( config['new_config']['ob_id']) if 'ob_id' in config['new_config'] else None, "channel_index" : str(channel_index) if channel_index is not None else None }) + if not bidir: break if 'is_opticalband' in config and config['is_opticalband']: is_optical_band=config['is_opticalband'] #channels = [channel['name']['index'] for channel in config['channels']] @@ -412,7 +484,7 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): optical_bands.append ({ "channel_uuid" : channel_get_uuid(f'optical_bands_{channel_index}',device_uuid), 'connection_uuid' : config['connection_uuid'], - "ob_uuid" : ob_get_uuid (f'ob_{channel_index}' ), + "ob_uuid" : ob_get_uuid (channel_index), 'created_at':now }) @@ -453,6 +525,18 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig): ) stmt = stmt.returning(TransponderTypeModel.transponder_uuid) transponder_id = session.execute(stmt).fetchone() + if (len(interfaces)>0) : + model_columns = inspect(TransponderInterfaceModel).c.keys() + stmt = insert(TransponderInterfaceModel).values(interfaces) + + stmt = stmt.on_conflict_do_update( + index_elements=[TransponderInterfaceModel.interface_uuid ], + set_={field: stmt.excluded[field] for field in model_columns if field != 'interface_uuid' } + + ) + stmt = stmt.returning(TransponderInterfaceModel.interface_uuid) + interface_id = session.execute(stmt).fetchone() + if len(channels) > 0: stmt = insert(OpticalChannelModel).values(channels) @@ -534,30 +618,33 @@ def delete_opticalchannel(db_engine : Engine, messagebroker : MessageBroker, req opticalconfig_uuid = request.opticalconfig_id.opticalconfig_uuid channels = [] config_type = None - + if "type" in config : config_type= config["type"] if 'new_config' in config: - if config_type == DeviceTypeEnum.OPTICAL_TRANSPONDER._value_: - for flow in config['flow']: - src,dest = flow - channel_index= src if src is not None and src!='0' else dest - channel_name= f"channel-{channel_index}" - channels.append({ - # "opticalconfig_uuid":opticalconfig_uuid, - "transponder_uuid" : transponder_get_uuid(device_id), - "channel_uuid" : channel_get_uuid(channel_name ,device_uuid), - "channel_name" : channel_name , - "frequency" : 0, - "operational_mode" : None, - "target_output_power": None, - "status" : "DISABLED" - }) - elif config_type == DeviceTypeEnum.OPTICAL_ROADM._value_: - channel_num = 0 - if 'flow' in config : - if 'is_opticalband' in config: - if config['is_opticalband']: + new_cfg = config['new_config'] + flow_id = new_cfg['flow_id'] if 'flow_id' in new_cfg else 0 + if 'flow' in config: + if config_type == DeviceTypeEnum.OPTICAL_TRANSPONDER._value_: + for flow in config['flow']: + src,dest = flow + channel_index= src if src is not None and src!='0' else dest + channel_name= f"channel-{channel_index}" + channels.append({ + # "opticalconfig_uuid":opticalconfig_uuid, + "transponder_uuid" : transponder_get_uuid(device_id), + "channel_uuid" : channel_get_uuid(channel_name ,device_uuid), + "channel_name" : channel_name , + "frequency" : 0, + "operational_mode" : None, + "target_output_power": None, + "status" : "DISABLED" + }) + elif config_type == DeviceTypeEnum.OPTICAL_ROADM._value_: + channel_num = flow_id + + if 'is_opticalband' in config : + if config['is_opticalband']: ob_id = config['new_config']['ob_id'] if 'ob_id' in config['new_config'] else None if len(config['flow']) == 0: channel_index = ob_id + channel_num @@ -569,16 +656,19 @@ def delete_opticalchannel(db_engine : Engine, messagebroker : MessageBroker, req channel_num +=1 channel_name = f'optical_bands_{channel_index}' channels.append(channel_get_uuid(channel_name, device_uuid)) - else: - if config['flow'] == 0: - channel_num = 1 + else : + + if len(config['flow']) == 0: + channel_name = f'media_channel_{channel_num}' channels.append(channel_get_uuid(channel_name, device_uuid)) else: for flow in config['flow']: - channel_num += 1 channel_name = f'media_channel_{channel_num}' channels.append(channel_get_uuid(channel_name, device_uuid)) + channel_num += 1 + + LOGGER.info(f"channels to delete {channels}") def callback(session : Session): all_suceed = [] diff --git a/src/context/service/database/models/OpticalConfig/OpticalBandModel.py b/src/context/service/database/models/OpticalConfig/OpticalBandModel.py index 2b205acc22a6c094f49e2c730e64a9eddb3544ff..2ff4ae97fc2302616f871d2b751a6ddfe262059c 100644 --- a/src/context/service/database/models/OpticalConfig/OpticalBandModel.py +++ b/src/context/service/database/models/OpticalConfig/OpticalBandModel.py @@ -26,8 +26,8 @@ class OpticalBandModel(_Base): ob_uuid = Column(UUID(as_uuid=False), primary_key=True) connection_uuid = Column(ForeignKey('connection.connection_uuid', ondelete='CASCADE'), nullable=False) - channel_uuid = Column(ForeignKey('channel.channel_uuid', ondelete='RESTRICT'), nullable=False) - created_at = Column(DateTime, nullable=False) + channel_uuid = Column(ForeignKey('channel.channel_uuid', ondelete='CASCADE'), nullable=False) + created_at = Column(DateTime, nullable=False) ob_channel = relationship('ChannelModel') # back_populates='connections' @@ -41,6 +41,10 @@ class OpticalBandModel(_Base): 'opticalband_id' : self.dump_id(), 'channel_id' : self.ob_channel.dump_id(), 'connection_id' : self.ob_connection.dump_id(), - 'service_id' : self.ob_connection.connection_service.dump_id() + 'service_id' : self.ob_connection.connection_service.dump_id(), + 'channel': json.dumps(self.ob_channel.dump()), + 'connection':self.ob_connection.dump(), + "service" : self.ob_connection.connection_service.dump() + } diff --git a/src/context/service/database/models/OpticalConfig/RoadmModel.py b/src/context/service/database/models/OpticalConfig/RoadmModel.py index 92a13ade00c806d0b29fe65dc81733193f2873ab..c6b374f22e7940b56dc30e018b961eecfbd312f9 100644 --- a/src/context/service/database/models/OpticalConfig/RoadmModel.py +++ b/src/context/service/database/models/OpticalConfig/RoadmModel.py @@ -59,7 +59,7 @@ class ChannelModel(_Base): def dump_id (self ): return { - "channel_uuid": self.channel_uuid + "channel_uuid": {"uuid":self.channel_uuid} } def dump(self): diff --git a/src/context/service/database/models/OpticalConfig/TransponderModel.py b/src/context/service/database/models/OpticalConfig/TransponderModel.py index a35476df34802dc91f50524ec9405e19ce5002c0..56b96e52410540ecf3da091ce83334de3cf4e8fc 100644 --- a/src/context/service/database/models/OpticalConfig/TransponderModel.py +++ b/src/context/service/database/models/OpticalConfig/TransponderModel.py @@ -23,9 +23,10 @@ class TransponderTypeModel (_Base): transponder_uuid = Column(String, primary_key=True) transcievers = Column(ARRAY(String), nullable=True) - interfaces = Column(String, nullable=True) + opticalconfig_uuid = Column(ForeignKey('optical_config.opticalconfig_uuid', ondelete='CASCADE'), index=True, nullable=False) - + + interfaces = relationship("TransponderInterfaceModel") channels = relationship("OpticalChannelModel") opticalconfig = relationship('OpticalConfigModel', back_populates='transponders') @@ -38,7 +39,7 @@ class TransponderTypeModel (_Base): return { "channels" : [channel.dump() for channel in self.channels], "transceivers" : {"transceiver": [transciever for transciever in self.transcievers]}, - "interfaces" : {"interface":json.loads(self.interfaces) if self.interfaces else ''}, + "interfaces" : [interface.dump() for interface in self.interfaces], "trasponder_uuid" : self.dump_id() } @@ -70,3 +71,63 @@ class OpticalChannelModel(_Base): "operational-mode" : self.operational_mode, "status" : self.status, } + + +class TransponderInterfaceModel(_Base): + __tablename__ = 'transponder_interface' + interface_uuid = Column(String, primary_key=True) + + name = Column (String,nullable=True) + status = Column(String , nullable=True) + operation_status = Column(String , nullable=True) + ifindex = Column(Integer, nullable=True) + in_octets = Column(Integer, nullable=True) + in_pkts = Column(Integer, nullable=True) + in_unicast_pkts = Column(Integer, nullable=True) + in_broadcast_pkts = Column(Integer, nullable=True) + in_multicast_pkts = Column(Integer, nullable=True) + out_discards = Column(Integer, nullable=True) + out_errors = Column(Integer, nullable=True) + in_discards = Column(Integer, nullable=True) + in_errors = Column(Integer, nullable=True) + out_octets = Column(Integer, nullable=True) + out_pkts = Column(Integer, nullable=True) + out_unicast_pkts = Column(Integer, nullable=True) + out_broadcast_pkts = Column(Integer, nullable=True) + out_multicast_pkts = Column(Integer, nullable=True) + + + last_clear = Column(Integer, nullable=True) + + + transponder_uuid = Column(ForeignKey('transponder_type.transponder_uuid', ondelete='CASCADE' ),nullable=False) + transponder = relationship('TransponderTypeModel',back_populates='interfaces') + # opticalconfig_uuid = Column(ForeignKey('optical_config.opticalconfig_uuid', ondelete='CASCADE' ), primary_key=True) + # opticalconfig = relationship('OpticalConfigModel', back_populates='channels') + def dump_id (self ): + return { + "interface_uuid":self.interface_uuid + } + + def dump(self): + return { + "name" :self.name, + "status":self.status, + "operation_status":self.operation_status, + "ifindex":self.ifindex, + "in_octets":self.in_octets, + "in_pkts":self.in_pkts, + "in_unicast_pkts":self.in_unicast_pkts, + "in_broadcast_pkts":self.in_broadcast_pkts, + "in_multicast_pkts":self.in_multicast_pkts, + "in_discards":self.in_discards, + "in_errors":self.in_errors, + "out_discards":self.out_discards, + "out_errors":self.out_errors, + "out_octets":self.out_octets, + "out_pkts":self.out_pkts, + "out_unicast_pkts":self.out_unicast_pkts, + "out_broadcast_pkts":self.out_broadcast_pkts, + "out_multicast_pkts":self.out_multicast_pkts, + "last_clear":self.last_clear + } diff --git a/src/context/service/database/uuids/OpticalConfig.py b/src/context/service/database/uuids/OpticalConfig.py index e11f835975fca86aba9e02eb6bbc15c6598627bb..1ac39761923d57f7eaacf08f32e470df0aa245db 100644 --- a/src/context/service/database/uuids/OpticalConfig.py +++ b/src/context/service/database/uuids/OpticalConfig.py @@ -83,7 +83,7 @@ def ob_get_uuid( if ( ob_name is not None): - result = get_uuid_from_string(ob_name) + result = get_uuid_from_string(f'ob_{ob_name}') logging.info(f'get_ob_uuid {result}') return result if allow_random: return get_uuid_random() @@ -91,3 +91,20 @@ def ob_get_uuid( raise InvalidArgumentsException([ ('ob_name ', ob_name), ], extra_details=['ob_name is required to produce a Optical band UUID']) + + + + +def interface_get_uuid( + interface_name :str , device_id:str, allow_random : bool = False +) -> str: + + + if len(interface_name) > 0: + return get_uuid_from_string(interface_name) + device_id + if allow_random: return get_uuid_random() + + raise InvalidArgumentsException([ + ('interface uuid', interface_name), + + ], extra_details=['Interface name is required to produce a interface UUID']) diff --git a/src/device/service/OpenConfigServicer.py b/src/device/service/OpenConfigServicer.py index fd0a8cde9a078720a2558e3af43dc62142df3c84..dcfbc96f43756dba33dfc9dda3e1819769e0d54a 100644 --- a/src/device/service/OpenConfigServicer.py +++ b/src/device/service/OpenConfigServicer.py @@ -89,16 +89,19 @@ class OpenConfigServicer(DeviceServiceServicer): device = get_device( context_client, device_uuid, rw_copy=True, include_endpoints=True, include_components=False, include_config_rules=False) + LOGGER.info(f"get_device {device.name}") if device is None: raise NotFoundException('Device', device_uuid, extra_details='loading in ConfigureDevice') resources, conditions = extract_resources(config=config, device=device) - + LOGGER.info(f"resources {resources}") + LOGGER.info(f"conditions {conditions}") driver : _Driver = get_driver(self.driver_instance_cache, device) results = driver.SetConfig(resources=resources,conditions=conditions) for result in results: - if not result : + if result is not None: is_all_good = False + raise Exception(result) if is_all_good: #driver.GetConfig(resource_keys=[]) @@ -136,7 +139,7 @@ class OpenConfigServicer(DeviceServiceServicer): #TODO: add a control with the NETCONF get #driver.GetConfig(resource_keys=filter_fields) except Exception as e: - LOGGER.info("error in configuring %s",e) + raise Exception("error in configuring %s",e) context_client.close() return Empty() @@ -147,7 +150,7 @@ class OpenConfigServicer(DeviceServiceServicer): resources : list[dict] = [] is_all_good = True config = json.loads(request.config) - + LOGGER.info(f"from disable optical device {config}") try: context_client = ContextClient() device = get_device( @@ -160,19 +163,13 @@ class OpenConfigServicer(DeviceServiceServicer): resources, conditions = extract_resources(config=config, device=device) driver : _Driver = get_driver(self.driver_instance_cache, device) - if 'edit_type' in conditions and conditions['edit_type'] == 'optical-band': - roadm_configuration = driver.GetConfig() - for resource_data in roadm_configuration: - resource_key, resource_value = resource_data - if resource_key.startswith('/opticalconfigs/opticalconfig/'): - roadm_configuration=resource_value["opticalconfig"] - results = driver.DeleteConfig(resources=resources,conditions=conditions,optical_device_configuration=roadm_configuration) + results = driver.DeleteConfig(resources=resources,conditions=conditions) for result in results: - if not result : + if result is not None: is_all_good = False - + raise Exception(result) if is_all_good: - config = json.loads(request.config) + if "new_config" in config : context_client.DeleteOpticalChannel(request) context_client.close() diff --git a/src/device/service/Tools.py b/src/device/service/Tools.py index e588c446f499955b757b99630b11b0f8f556fde6..c8e32734513a1a113c60a3b4379fc0edf98fccd3 100644 --- a/src/device/service/Tools.py +++ b/src/device/service/Tools.py @@ -505,6 +505,7 @@ def extract_resources(config : dict, device : Device) -> list[list[dict], dict]: resources.append(is_key_existed('administrative-state', keys_dic=config['new_config'])) resources.append(is_key_existed('frequency', keys_dic=config['new_config'])) resources.append(is_key_existed('width', keys_dic=config['new_config'])) + for port in ports_dic["value"]: circuit_pack_dic = is_key_existed('supporting-circuit-pack-name', keys_dic=port) interface_list = is_key_existed('supporting-interface-list', keys_dic=port) @@ -551,6 +552,7 @@ def extract_resources(config : dict, device : Device) -> list[list[dict], dict]: 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('bidir', keys_dic=config['new_config'])) #resources.append(is_key_existed('name', keys_dic=config['new_config'], key_name_to_use='channel_name')) if not is_opticalband: diff --git a/src/device/service/drivers/oc_driver/OCDriver.py b/src/device/service/drivers/oc_driver/OCDriver.py index 5fede091ff6d9f2057d05582a810b5f8e73d60cf..bfe3d4b2fe60f05bcea42d7315eb4dc61f141690 100644 --- a/src/device/service/drivers/oc_driver/OCDriver.py +++ b/src/device/service/drivers/oc_driver/OCDriver.py @@ -28,7 +28,7 @@ from device.service.driver_api._Driver import _Driver from .templates import compose_config, cli_compose_config, ufi_interface, cisco_interface from .templates.VPN.common import seperate_port_config from .templates.VPN.roadms import ( - create_optical_band, disable_media_channel, delete_optical_band, create_media_channel_v2 + create_optical_band, disable_media_channel, delete_optical_band, create_media_channel ) from .templates.VPN.transponder import edit_optical_channel, change_optical_channel_status from .RetryDecorator import retry @@ -133,8 +133,8 @@ class NetconfSessionHandler: str_respones = str(response) if re.search(r'<ok/>', str_respones): - return True - return False + return None + return str_respones @RETRY_DECORATOR def locked(self, target): @@ -171,7 +171,7 @@ def edit_config( str_config_messages = network_media_channel_handler(resources) else : #roadm media-channel - str_config_messages=create_media_channel_v2(resources) + str_config_messages=create_media_channel(resources) #Disabling of the Configuration else: # Device type is Transponder @@ -333,8 +333,9 @@ class OCDriver(_Driver): extracted_values = transponder_values_extractor( data_xml=xml_data, resource_keys=transponder_filter_fields, dic=config ) - transceivers, optical_channels_params, channel_namespace, endpoints, ports_result = extracted_values + interfaces,optical_channels_params,channel_namespace,endpoints,ports_result=extracted_values oc_values["channels" ] = optical_channels_params + oc_values["interfaces" ] =interfaces oc_values["transceivers" ] = transceivers oc_values["channel_namespace"] = channel_namespace oc_values["endpoints" ] = endpoints @@ -370,6 +371,7 @@ class OCDriver(_Driver): @metered_subclass_method(METRICS_POOL) def SetConfig(self, resources : List[Tuple[str, Any]],conditions:dict) -> List[Union[bool, Exception]]: + logging.info(f'from driver conditions {conditions}') if len(resources) == 0: return [] results = [] with self.__lock: @@ -388,7 +390,7 @@ class OCDriver(_Driver): @metered_subclass_method(METRICS_POOL) def DeleteConfig( self, resources : List[Tuple[str, Any]], conditions : dict, - optical_device_configuration = None + ) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] diff --git a/src/device/service/drivers/oc_driver/templates/VPN/common.py b/src/device/service/drivers/oc_driver/templates/VPN/common.py index 15d180bf826f906a70e3d8bc543ef38ccd2f0dba..a3b9e3b43d41d6f71253dbccebbe3b2bd64eff7d 100644 --- a/src/device/service/drivers/oc_driver/templates/VPN/common.py +++ b/src/device/service/drivers/oc_driver/templates/VPN/common.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging + def seperate_port_config(resources:list,unwanted_keys=[])->list[list,dict,str]: config=[] @@ -27,7 +29,7 @@ def seperate_port_config(resources:list,unwanted_keys=[])->list[list,dict,str]: ports[item['resource_key']]=item['value'] if (item['resource_key']=='index' and item['value'] is not None) : index=item['value'] - + logging.info(f"seperate_port_config {ports}") return [config,ports,index] def extract_ports (resources:list): @@ -51,5 +53,5 @@ def filter_config(resources:list,unwanted_keys=[])->list[list,dict,str]: #if (item['resource_key'] == 'destination_port' or item['resource_key'] == 'source_port') and item['value'] is not None: # ports[item['resource_key']]=item['value'] ports = extract_ports(resources=resources) - + logging.info(f"filter_config {ports}") return [config,ports,index] diff --git a/src/device/service/drivers/oc_driver/templates/VPN/roadms.py b/src/device/service/drivers/oc_driver/templates/VPN/roadms.py index ade87e1c554ca402de953e0986c9e63d54482bd9..efa0efc8d30dbe68329c99a53565b542a5a33142 100644 --- a/src/device/service/drivers/oc_driver/templates/VPN/roadms.py +++ b/src/device/service/drivers/oc_driver/templates/VPN/roadms.py @@ -21,123 +21,23 @@ from .common import seperate_port_config ,filter_config -def create_media_channel_old (resources): - optical_band_namespaces="http://flex-scale-project.eu/yang/flex-scale-mg-on" - results=[] - unwanted_keys=['destination_port','source_port','channel_namespace' - ,'frequency','operational-mode','target-output-power', - "admin-state","flow_handled","channel_num"] - config,ports,index= seperate_port_config(resources,unwanted_keys=unwanted_keys) - - doc, tag, text = Doc().tagtext() - #with tag('config'): - with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): - with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): - with tag('media-channels'): - n = 0 - if 'destination_port' in ports: - n = len(ports['destination_port']) - else: - n = len(ports['source_port']) - for i in range(0, n): - #with tag('channel', operation="create"): - with tag('channel'): - with tag('index'):text(str(int(index)+i)) - with tag('config'): - #with tag('index'):text(index) - for resource in config: - - if resource['resource_key'] == "index": - with tag('index'):text(str(int(index)+i)) - elif resource['resource_key']== 'optical-band-parent' : - with tag('optical-band-parent',xmlns=optical_band_namespaces):text(resource['value']) - else: - with tag(resource['resource_key']):text(resource['value']) - if ('destination_port' in ports) and (ports['destination_port'][i] is not None): - with tag('dest'): - with tag('config'): - with tag('port-name'):text(ports['destination_port'][i]) - if ('source_port' in ports) and (ports['source_port'][i] is not None): - with tag('source'): - with tag('config'): - with tag('port-name'):text(ports['source_port'][i]) - - - result = indent( - doc.getvalue(), - indentation = ' '*2, - newline = '' - ) - results.append(result) - return results - - - - -def create_media_channel (resources): - optical_band_namespaces="http://flex-scale-project.eu/yang/flex-scale-mg-on" - results=[] - unwanted_keys=['destination_port','source_port','channel_namespace' - ,'frequency','operational-mode','target-output-power', - "admin-state","handled_flow","channel_num"] - - config,ports,index=filter_config(resources,unwanted_keys) - - doc, tag, text = Doc().tagtext() - #with tag('config'): - with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): - with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): - with tag('media-channels'): - n = 0 - - for flow in ports: - src,dest=flow - #with tag('channel', operation="create"): - with tag('channel'): - with tag('index'):text(str(int(index)+n)) - with tag('config'): - #with tag('index'):text(index) - for resource in config: - - if resource['resource_key'] == "index": - with tag('index'):text(str(int(index)+n)) - elif resource['resource_key']== 'optical-band-parent' : - with tag('optical-band-parent',xmlns=optical_band_namespaces):text(resource['value']) - else: - with tag(resource['resource_key']):text(resource['value']) - if dest is not None and dest != '0': - with tag('dest'): - with tag('config'): - with tag('port-name'):text(dest) - if src is not None and src != '0': - with tag('source'): - with tag('config'): - with tag('port-name'):text(src) - n+=1 - - result = indent( - doc.getvalue(), - indentation = ' '*2, - newline = '' - ) - results.append(result) - return results - - -def create_media_channel_v2 (resources): +def create_media_channel (resources): optical_band_namespaces="http://flex-scale-project.eu/yang/flex-scale-mg-on" results=[] unwanted_keys=['destination_port','source_port','channel_namespace' ,'frequency','operational-mode','target-output-power', - "handled_flow","channel_num"] + "handled_flow","channel_num",'bidir'] + bidir = next(i['value'] for i in resources if i['resource_key'] == 'bidir') config,ports,index=filter_config(resources,unwanted_keys) + logging.info(f'bidir {bidir}') n = 0 + for flow in ports: doc, tag, text = Doc().tagtext() with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): @@ -156,13 +56,13 @@ def create_media_channel_v2 (resources): if resource['resource_key'] == "index": with tag('index'):text(str(int(index)+n)) elif resource['resource_key']== 'optical-band-parent' : - with tag('optical-band-parent',xmlns=optical_band_namespaces):text(int(resource['value'])) + with tag('optical-band-parent',xmlns=optical_band_namespaces):text(int(resource['value'])+int(n)) elif resource['resource_key']== 'admin-state' : with tag('admin-status'):text(resource['value']) else: with tag(resource['resource_key']):text(resource['value']) - + if src is not None and src != '0': with tag('source'): with tag('config'): @@ -170,8 +70,11 @@ def create_media_channel_v2 (resources): if dest is not None and dest != '0': with tag('dest'): with tag('config'): - with tag('port-name'):text(dest) + with tag('port-name'):text(dest) + + n+=1 + result = indent( doc.getvalue(), @@ -179,65 +82,16 @@ def create_media_channel_v2 (resources): newline = '' ) results.append(result) + if not bidir : break return results - - - -def create_optical_band_old (resources) : - results =[] - unwanted_keys=['destination_port','source_port','channel_namespace','frequency','optical-band-parent','flow_handled'] - config,ports,index= seperate_port_config(resources,unwanted_keys=unwanted_keys) - - doc, tag, text = Doc().tagtext() - #with tag('config'): - with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): - with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): - with tag('optical-bands',xmlns="http://flex-scale-project.eu/yang/flex-scale-mg-on"): - n = 0 - if 'destination_port' in ports: - n = len(ports['destination_port']) - else: - n = len(ports['source_port']) - for i in range(0, n): - #with tag('optical-band', operation="create"): - with tag('optical-band'): - if index is not None: - with tag('index'):text(str(int(index)+i)) - with tag('config'): - #if index is not None: - # with tag('index'):text(str(int(index)+i)) - for resource in config: - if resource['resource_key'] == "index": - with tag('index'):text(str(int(index)+i)) - else: - with tag(resource['resource_key']):text(resource['value']) - with tag('admin-status'):text('ENABLED') - #with tag('fiber-parent'):text(ports['destination_port'] if 'destination_port' in ports else ports['source_port']) - if ('destination_port' in ports) and (ports['destination_port'][i] is not None): - with tag('dest'): - with tag('config'): - with tag('port-name'):text(ports['destination_port'][i]) - if ('source_port' in ports) and (ports['source_port'][i] is not None): - with tag('source'): - with tag('config'): - with tag('port-name'):text(ports['source_port'][i]) - - - result = indent( - doc.getvalue(), - indentation = ' '*2, - newline = '' - ) - results.append(result) - return results - - def create_optical_band (resources) : results =[] - unwanted_keys=['destination_port','source_port','channel_namespace','frequency','optical-band-parent','handled_flow'] + unwanted_keys=['destination_port','source_port','channel_namespace' + ,'frequency','optical-band-parent' + ,'handled_flow','bidir'] config,ports,index= filter_config(resources,unwanted_keys=unwanted_keys) #with tag('config'): @@ -288,7 +142,12 @@ def create_optical_band (resources) : def disable_media_channel (resources): results=[] - unwanted_keys=['destination_port','source_port','channel_namespace','frequency','operational-mode', 'optical-band-parent'] + bidir = next(i['value'] for i in resources if i['resource_key'] == 'bidir') + unwanted_keys=['destination_port','source_port' + ,'channel_namespace','frequency' + ,'operational-mode', 'optical-band-parent' + ,'bidir' + ] config,ports,index= seperate_port_config(resources,unwanted_keys=unwanted_keys) n = 0 @@ -324,11 +183,15 @@ def disable_media_channel (resources): newline = '' ) results.append(result) + if not bidir: break return results def disable_optical_band (resources:list,state:str): results=[] - unwanted_keys=['destination_port','source_port','channel_namespace','frequency','operational-mode', 'optical-band-parent'] + unwanted_keys=['destination_port','source_port' + ,'channel_namespace','frequency' + ,'operational-mode', 'optical-band-parent' + ,"bidir"] config,ports,index= seperate_port_config(resources,unwanted_keys=unwanted_keys) doc, tag, text = Doc().tagtext() #with tag('config'): @@ -347,7 +210,51 @@ def disable_optical_band (resources:list,state:str): newline = '' ) results.append(result) - return results + return results + + + + +def disable_optical_band_v2 (resources:list,state:str): + results=[] + unwanted_keys=['destination_port','source_port','channel_namespace','frequency','operational-mode', 'optical-band-parent'] + config,ports,index= seperate_port_config(resources,unwanted_keys=unwanted_keys) + n = 0 + for flow in ports: + doc, tag, text = Doc().tagtext() + with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): + with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): + with tag('optical-bands',xmlns="http://flex-scale-project.eu/yang/flex-scale-mg-on"): + with tag('optical-band',operation="delete"): + if index is not None: + with tag('index'):text(str(int(index) + n)) + with tag('config'): + with tag('index'):text(str(int(index) + n)) + + n +=1 + + + ''' + doc, tag, text = Doc().tagtext() + #with tag('config'): + with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): + with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): + with tag('optical-bands',xmlns="http://flex-scale-project.eu/yang/flex-scale-mg-on"): + with tag('optical-band',operation="delete"): + if index is not None: + with tag('index'):text(index) + with tag('config'): + with tag('index'):text(index) + + ''' + result = indent( + doc.getvalue(), + indentation = ' '*2, + newline = '' + ) + results.append(result) + return results + def delete_optical_band (resources:list): @@ -374,7 +281,11 @@ def delete_optical_band (resources:list): # return results n = 0 - for flow in ports: + for key,v in ports.items(): + if isinstance(v,list): + if len(v)==1 and v[0] is None : continue + else : + if v is None : continue doc, tag, text = Doc().tagtext() with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): diff --git a/src/device/service/drivers/oc_driver/templates/discovery_tool/transponders.py b/src/device/service/drivers/oc_driver/templates/discovery_tool/transponders.py index 789dd17065adc06fe0ceca0c42d2c69d599235cf..65e737d6aa942e59333136699fe1902df5b98a70 100644 --- a/src/device/service/drivers/oc_driver/templates/discovery_tool/transponders.py +++ b/src/device/service/drivers/oc_driver/templates/discovery_tool/transponders.py @@ -210,8 +210,6 @@ def extract_interfaces (xml_data:str): xml_bytes = xml_data.encode("utf-8") root = ET.fromstring(xml_bytes) - - namespace = {'oc': 'http://openconfig.net/yang/interfaces'} interfaces_result = [] interfaces = root.findall('.//oc:interface',namespace) @@ -286,6 +284,7 @@ def extract_interfaces (xml_data:str): return interfaces_result + def has_opticalbands(xml_data:str): xml_bytes = xml_data.encode("utf-8") @@ -349,7 +348,7 @@ def transponder_values_extractor(data_xml:str,resource_keys:list,dic:dict): endpoints.append({"endpoint_uuid":{"uuid":channel_name}}) optical_channels_params.append(dic) #transceivers_dic=extract_tranceiver(data_xml=data_xml,dic={}) - #transceivers_dic={"transceiver":[]} + transceivers_dic={"transceiver":[]} interfaces=extract_interfaces(xml_data=data_xml) if len(ports)>0 : for port in ports : @@ -359,4 +358,4 @@ def transponder_values_extractor(data_xml:str,resource_keys:list,dic:dict): ports_result.append((resource_key, resource_value)) - return [transceivers_dic,optical_channels_params,channel_namespace,endpoints,ports_result] + return [interfaces,optical_channels_params,channel_namespace,endpoints,ports_result] diff --git a/src/opticalcontroller/OpticalController.py b/src/opticalcontroller/OpticalController.py index 4610b9ffe0c776572147fb6debc24e46c4dc6bf8..0b711aa7d11c05118ac6d5cc5c48fc73c93a1295 100644 --- a/src/opticalcontroller/OpticalController.py +++ b/src/opticalcontroller/OpticalController.py @@ -1,4 +1,4 @@ -# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,19 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging, time from flask import Flask from flask import render_template from flask_restplus import Resource, Api -from google.protobuf.json_format import MessageToDict -from common.proto.context_pb2 import TopologyId from opticalcontroller.tools import * from opticalcontroller.variables import * from opticalcontroller.RSA import RSA - -logging.basicConfig(level=logging.INFO) -LOGGER = logging.getLogger(__name__) - +import time , logging +from common.proto.context_pb2 import TopologyId , OpticalLink +import json +from google.protobuf.message import Message +from google.protobuf.json_format import MessageToDict +from common.tools.object_factory.OpticalLink import order_dict global rsa global links_dict rsa = None @@ -97,20 +96,15 @@ class AddFlexLightpath(Resource): return rsa.db_flows[flow_id], 200 else: if len(rsa.optical_bands[optical_band_id]["flows"]) == 0: - print('No Path Found ') - return 'No path found', 404 else: t1 = time.time() * 1000.0 elapsed = t1 - t0 print("INFO: time elapsed = {} ms".format(elapsed)) - - + return rsa.optical_bands[optical_band_id], 200 else: return "Error", 404 - - # @optical.route('/DelFlexLightpath/<string:src>/<string:dst>/<int:bitrate>/<int:o_band_id>') @optical.route('/DelFlexLightpath/<string:src>/<string:dst>/<int:bitrate>/<int:o_band_id>') @optical.route('/DelFlexLightpath/<string:src>/<string:dst>/<int:bitrate>/<int:o_band_id>/<int:flow_id>') @@ -119,18 +113,21 @@ class AddFlexLightpath(Resource): class DelFLightpath(Resource): @staticmethod def delete( src, dst, bitrate, o_band_id, flow_id=None): - flow = None - match1 = False - ob_id = None + flow = None + match1=False + ob_id=None if flow_id is not None: + if flow_id in rsa.db_flows.keys(): - flow = rsa.db_flows[flow_id] - match1 = flow["src"] == src and flow["dst"] == dst and flow["bitrate"] == bitrate - ob_id = flow["parent_opt_band"] - flow['is_active'] = False - + flow = rsa.db_flows[flow_id] + match1 = flow["src"] == src and flow["dst"] == dst and flow["bitrate"] == bitrate + ob_id = flow["parent_opt_band"] + flow['is_active']=False if flow is not None: + + bidir = flow["bidir"] + if bidir: match2 = flow["src"] == dst and flow["dst"] == src and flow["bitrate"] == bitrate if match1 or match2: @@ -150,6 +147,7 @@ class DelFLightpath(Resource): return "flow {} not matching".format(flow_id), 404 else: if match1: + # if delete_band !=0 and ob_id is not None: # print(f"delete_lightpath {delete_band} and ob_id {ob_id}") # if len( rsa.optical_bands[ob_id]["served_lightpaths"]) != 0: @@ -166,24 +164,32 @@ class DelFLightpath(Resource): return "flow {} not matching".format(flow_id), 404 else: return "flow id {} does not exist".format(flow_id), 404 + + + -@optical.route('/DelOpticalBand/<string:src>/<string:dst>/<int:o_band_id>', methods=['DELETE']) +@optical.route('/DelOpticalBand/<string:src>/<string:dst>/<int:o_band_id>',methods=['DELETE']) @optical.response(200, 'Success') @optical.response(404, 'Error, not found') class DelOpticalBand(Resource): @staticmethod def delete( src, dst, o_band_id): flow = None - ob_id = None - if o_band_id is not None: - if o_band_id in rsa.optical_bands.keys(): - flow = rsa.optical_bands[o_band_id] - match1 = flow["src"] == src and flow["dst"] == dst and flow["bitrate"] - ob_id = o_band_id + ob_id=None + if o_band_id is not None: + + if o_band_id in rsa.optical_bands.keys(): + flow=rsa.optical_bands[o_band_id] + #match1 = flow["src"] == src and flow["dst"] == dst + ob_id=o_band_id + if flow is not None: + + bidir = flow["bidir"] + if bidir: match1 = flow["src"] == src and flow["dst"] == dst and flow["bitrate"] match2 = flow["src"] == dst and flow["dst"] == src and flow["bitrate"] @@ -191,7 +197,7 @@ class DelOpticalBand(Resource): ob_id = flow["parent_opt_band"] #rsa.del_flow(flow, ob_id) rsa.optical_bands[ob_id]["is_active"] = False - # if flow_id in rsa.optical_bands[ob_id]["served_lightpaths"]: + # if flow_id in rsa.optical_bands[ob_id]["served_lightpaths"].remove: # rsa.optical_bands[ob_id]["served_lightpaths"].remove(flow_id) #if rsa.optical_bands[ob_id]["reverse_optical_band_id"] != 0: # rev_ob_id = rsa.optical_bands[ob_id]["reverse_optical_band_id"] @@ -297,7 +303,6 @@ class GetFlows(Resource): except: return "Error", 404 - @optical.route('/GetOpticalBands') @optical.response(200, 'Success') @optical.response(404, 'Error, not found') @@ -343,8 +348,8 @@ class GetFlows(Resource): return links, 200 except: return "Error", 404 - - + + @optical.route('/GetTopology/<path:context_id>/<path:topology_id>',methods=['GET']) @optical.response(200, 'Success') @optical.response(404, 'Error, not found') @@ -360,46 +365,57 @@ class GetTopology(Resource): topog_id.topology_uuid.uuid=topology_id topog_id.context_id.context_uuid.uuid=context_id - try: - links_dict = {"optical_links": []} - node_dict = {} - topo, nodes = readTopologyDataFromContext(topog_id) - - for link in topo: - link_dict_type = MessageToDict(link, preserving_proto_field_name=True) - - if "c_slots" in link_dict_type["optical_details"]: - link_dict_type["optical_details"]["c_slots"] = link_dict_type["optical_details"]["c_slots"] - - if "l_slots" in link_dict_type["optical_details"]: - link_dict_type["optical_details"]["l_slots"] = link_dict_type["optical_details"]["l_slots"] - - if "s_slots" in link_dict_type["optical_details"]: - link_dict_type["optical_details"]["s_slots"] = link_dict_type["optical_details"]["s_slots"] - - links_dict["optical_links"].append(link_dict_type) - - for device in nodes : - dev_dic = { - "id":device.device_id.device_uuid.uuid, - #"ip":f"10.30.2.{207+i}", - #"port":"50001", - "type":"OC-ROADM" if device.device_type =="optical-roadm" else "OC-TP", - "driver": "OpticalOC" - } - node_dict[device.name] = dev_dic - #i+=1 - #print(f"refresh_optical controller optical_links_dict= {links_dict}") - #print(f"refresh_optical controller node_dict {node_dict}") - - rsa = RSA(node_dict, links_dict) - if debug: - print(rsa.init_link_slots2()) - return "ok", 200 + try: + links_dict={"optical_links":[]} + node_dict = {} + topo , nodes = readTopologyDataFromContext(topog_id) + + for link in topo: + link_dict_type = MessageToDict(link, preserving_proto_field_name=True) + + if "c_slots" in link_dict_type["optical_details"] : + link_dict_type["optical_details"]["c_slots"]=order_dict(link_dict_type["optical_details"]["c_slots"]) + + if "l_slots" in link_dict_type["optical_details"] : + link_dict_type["optical_details"]["l_slots"]=order_dict(link_dict_type["optical_details"]["l_slots"]) + + if "s_slots" in link_dict_type["optical_details"] : + link_dict_type["optical_details"]["s_slots"]=order_dict(link_dict_type["optical_details"]["s_slots"]) + + links_dict["optical_links"].append(link_dict_type) + + + + for device in nodes : + dev_dic = {} + dev_dic = { + "id":device.device_id.device_uuid.uuid, + #"ip":f"10.30.2.{207+i}", + #"port":"50001", + "type":"OC-ROADM" if device.device_type =="optical-roadm" else "OC-TP", + "driver": "OpticalOC" + } + node_dict[device.name]=dev_dic + #i+=1 + #print(f"refresh_optical controller optical_links_dict= {links_dict}") + #print(f"refresh_optical controller node_dict {node_dict}") + + rsa = RSA(node_dict, links_dict) + if debug: + print(f'rsa.init_link_slots2() {rsa}') + print(rsa.init_link_slots2()) + + + return "ok" ,200 except Exception as e: - LOGGER.exception('Error in GetTopology') print(f"err {e}") return "Error", 400 + + + if __name__ == '__main__': + + + app.run(host='0.0.0.0', port=10060) diff --git a/src/opticalcontroller/RSA.py b/src/opticalcontroller/RSA.py index d2c833de3d41f99273fbd78c2a45f50db83cc8ba..2395f594851dd61de4cbf43e3fc0b2fbbb73563f 100644 --- a/src/opticalcontroller/RSA.py +++ b/src/opticalcontroller/RSA.py @@ -1,4 +1,4 @@ -# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from opticalcontroller.dijkstra import Graph, shortest_path +from opticalcontroller.dijkstra import * from opticalcontroller.tools import * from opticalcontroller.variables import * @@ -31,8 +31,9 @@ class RSA(): self.l_slot_number = 0 self.s_slot_number = 0 self.optical_bands = {} - - def init_link_slots(self): + + + def init_link_slots(self, testing): if full_links: for l in self.links_dict["optical_links"]: for fib in l["optical_link"]["details"]["fibers"]: @@ -266,8 +267,18 @@ class RSA(): fib[band][str(i)] = 0 if 'used' in fib: fib['used'] = True - print(f"fib updated {fib}") + print(f"fib updated {fib}") + #print(fib) + + def update_link_2(self, fib, slots, band,link): #print(fib) + for i in slots: + fib[band][str(i)] = 0 + if 'used' in fib: + fib['used'] = True + + set_link_update(fib,link) + #print(fib) def update_optical_band(self, optical_band_id, slots, band): for i in slots: @@ -283,25 +294,43 @@ class RSA(): if 'used' in fib: fib['used'] = False #fib[band].sort() + + def restore_link_2(self, fib, slots, band, link): + print("start restoring link") + for i in slots: + fib[band][str(i)] = 1 + if 'used' in fib: + fib['used'] = False + #fib[band].keys().sort() + #set_link_update(fib,link,test="restoration") + def restore_optical_band(self, optical_band_id, slots, band): + for i in slots: self.optical_bands[optical_band_id][band][str(i)] = 1 + #self.optical_bands[optical_band_id][band].append(int(i)) #self.optical_bands[optical_band_id][band].sort() + + + + + def restore_optical_band_2(self, optical_band_id, slots, band ,links): print(f"example of band { band}") print(f"example of slots {slots}") print(f"example of self.optical_bands_before { self.optical_bands}") for i in slots: self.optical_bands[optical_band_id][band][str(i)] = 1 - print(f"example of self.optical_bands_after { self.optical_bands}") - + print(f"example of self.optical_bands_after { self.optical_bands}") + #link_name= self.optical_bands[optical_band_id]['links'][0] #link = self.get_link_by_name(link_name) #update_optical_band(optical_bands=self.optical_bands,optical_band_id=optical_band_id,band=band,link=link) - + + def del_flow(self, flow,flow_id, o_b_id = None): flows = flow["flows"] band = flow["band_type"] @@ -315,6 +344,7 @@ class RSA(): bidir = flow["bidir"] flow_id = flow["flow_id"] + for l in links: if debug: print(l) @@ -327,8 +357,11 @@ class RSA(): self.restore_link_2(fib, slots, band, link=link) if debug: print(fib[band]) - + + + if o_b_id is not None: + if debug: print("restoring OB") print(f"invoking restore_optical_band o_b_id: {o_b_id} , slots {slots} , band {band} ") @@ -338,7 +371,6 @@ class RSA(): self.optical_bands[o_b_id]["served_lightpaths"].remove(flow_id) #self.restore_optical_band_2(o_b_id, slots, band,links) - if bidir: for l in links: r_l = reverse_link(l) @@ -348,7 +380,8 @@ class RSA(): fib = rlink["optical_details"] #fib = self.get_link_by_name(r_l)["optical_details"] if list_in_list(slots, str_list_to_int(fib[band].keys())): - self.restore_link(fib, slots, band) + #self.restore_link(fib, slots, band, link=l) + self.restore_link_2(fib, slots, band, link=rlink) if debug: print(fib[band]) ''' @@ -373,7 +406,9 @@ class RSA(): def del_band(self, flow, o_b_id = None): + print(f"delete band {flow} ") + flows = flow["flows"] band = None @@ -397,22 +432,14 @@ class RSA(): slots.sort() - for l in links: - if debug: - print(l) - #link = self.links_dict[l] - #f = fiber_f[l] - #fib = link['fibers'][f] - fib = self.get_link_by_name(l)["optical_details"] - print(f"del_flow_fib {fib } and band {band}") - print(f"del_flow { str_list_to_int(fib[band].keys())}") - - print(f"invoking restore_link fib: {fib} , slots {slots} , band {band} ") - self.restore_link(fib, slots, band) - self.optical_bands[o_b_id]["is_active"]=False - - if debug: - print(fib[band]) + for l in links: + if debug: + print(l) + #fib = self.get_link_by_name(l)["optical_details"] + link = self.get_link_by_name(l) + fib = link["optical_details"] + print(f"del_flow_fib {fib } and band {band}") + print(f"del_flow { str_list_to_int(fib[band].keys())}") print(f"invoking restore_link_2 fib: {fib} , slots {slots} , band {band} ") #self.restore_link(fib, slots, band) @@ -444,7 +471,8 @@ class RSA(): # rev_o_band_id = self.optical_bands[o_b_id]["reverse_optical_band_id"] # self.restore_optical_band(rev_o_band_id, slots, band) return True - + + def del_handler(self, flow,flow_id, o_b_id = None,delete_band=0): print(f" del_handler flow {flow} flow_id {flow_id} o_b_id {o_b_id} delete_band {delete_band}") if delete_band != 0: @@ -452,7 +480,9 @@ class RSA(): self.del_band(flow,flow_id,o_b_id=o_b_id) else : self.del_flow(flow,flow_id=flow_id,o_b_id=o_b_id) - + + + def get_fibers_forward(self, links, slots, band): fiber_list = {} add = links[0] @@ -487,7 +517,8 @@ class RSA(): continue if list_in_list(slots, str_list_to_int(fib[band].keys())): #fiber_list[l] = fib["ID"] - self.update_link(fib, slots, band) + #self.update_link(fib, slots, band) + self.update_link_2(fib,slots,band,link) break print("INFO: Path forward computation completed") return fiber_list @@ -629,14 +660,13 @@ class RSA(): #function ivoked for fs lightpaths only def select_slots_and_ports_fs(self, links, n_slots, c, l, s, bidir, o_band_id): if debug: - print('select_slots_and_ports_fs links_dict',self.links_dict) - print('self.c_slot_number, self.l_slot_number, s_slot_number',self.c_slot_number, self.l_slot_number,self.s_slot_number) + print(self.links_dict) band, slots = slot_selection(c, l, s, n_slots, self.c_slot_number, self.l_slot_number, self.s_slot_number) if band is None: print("No slots available in the three bands") return None, None, None, None, None if debug: - print('band, slots',band, slots) + print(band, slots) self.get_fibers_forward(links, slots, band) if bidir: self.get_fibers_backward(links, slots, band) @@ -966,7 +996,7 @@ class RSA(): num_slots_ob = "full_band" if band is not None: num_slots_ob = map_band_to_slot(band) - print('band, num_slots_ob',band, num_slots_ob) + print(band, num_slots_ob) if self.nodes_dict[src]["type"] == "OC-ROADM" and self.nodes_dict[dst]["type"] == "OC-ROADM": print("INFO: ROADM to ROADM connection") links, path = self.compute_path(src, dst) @@ -1020,13 +1050,12 @@ class RSA(): continue op, num_slots = map_rate_to_slot(rate) if debug: - print('num_slots',num_slots) print(temp_links2) c_slots, l_slots, s_slots = self.get_slots(temp_links2, num_slots, ob_id) if debug: - print('c_slots',c_slots) - print('l_slots',l_slots) - print('s_slots',s_slots) + print(c_slots) + print(l_slots) + print(s_slots) if len(c_slots) >= num_slots or len(l_slots) >= num_slots or len(s_slots) >= num_slots: flow_list, band_range, slots, fiber_f, fiber_b = self.select_slots_and_ports_fs(temp_links2, num_slots, c_slots, @@ -1067,10 +1096,9 @@ class RSA(): return self.flow_id, ob_id else: print("not enough slots") - print("trying to extend OB {} and band {}".format(ob_id,band)) + print("trying to extend OB {}".format(ob_id)) new_slots = self.extend_optical_band(ob_id, band=None) - if debug : - print ('new_slots',new_slots) + if len(new_slots) > 0: band_type = self.optical_bands[ob_id]["band_type"] c_slots = [] @@ -1084,14 +1112,12 @@ class RSA(): s_slots = new_slots op, num_slots = map_rate_to_slot(rate) if debug: - print('num_slots2',num_slots) - - print('temp_links2',temp_links2) + print(temp_links2) c_slots, l_slots, s_slots = self.get_slots(temp_links2, num_slots, ob_id) if debug: - print('c_slots',c_slots) - print('l_slots',l_slots) - print('s_slots',s_slots) + print(c_slots) + print(l_slots) + print(s_slots) #print(c_slots) #print(l_slots) #print(s_slots) @@ -1103,8 +1129,8 @@ class RSA(): ob_id) f0, band = frequency_converter(band_range, slots) if debug: - print('f0, band',f0, band) - print("INFO: RSA completed Exetnding for Flex Lightpath with OB already in place") + print(f0, band) + print("INFO: RSA completed for Flex Lightpath with OB already in place") if flow_list is None: self.null_values(self.flow_id) continue @@ -1151,9 +1177,9 @@ class RSA(): print(temp_links) c_slots, l_slots, s_slots = self.get_slots(temp_links, num_slots, optical_band_id) if debug: - print('c_slots',c_slots) - print('l_slots',l_slots) - print('s_slots',s_slots) + print(c_slots) + print(l_slots) + print(s_slots) if len(c_slots) > 0 or len(l_slots) > 0 or len(s_slots) > 0: flow_list, band_range, slots, fiber_f, fiber_b = self.select_slots_and_ports_fs(temp_links, num_slots, c_slots, l_slots, s_slots, bidir, optical_band_id) @@ -1207,15 +1233,14 @@ class RSA(): link = self.get_link_by_name(l) fib = link["optical_details"][band_type] #s_slots = get_side_slots_on_link(link, band_type, num_slots_ob, slots) - #s_slots, s_num = get_side_slots_on_link(fib, num_slots_ob, slots) - s_slots,s_num = get_side_slots_on_link_v2(fib, num_slots_ob, slots) - print("NEW SLOTS {} and s_num {}".format(s_slots,s_num)) + s_slots, s_num = get_side_slots_on_link(fib, num_slots_ob, slots) + print("NEW SLOTS {}".format(s_slots)) if len(new_slots) == 0: new_slots = s_slots else: if len(new_slots) < s_num: new_slots = list_in_list(new_slots, s_slots) - print(" NEW SLOTS extend_optical_band {}".format(new_slots)) + print("NEW SLOTS {}".format(new_slots)) self.augment_optical_band(ob_id, new_slots, band_type) new_band = int(len(new_slots)*12.5*1000) print("{}, {},{},{} ".format(old_band, f0, len(new_slots), new_band)) @@ -1228,4 +1253,5 @@ class RSA(): link = self.get_link_by_name(link_x) fib = link["optical_details"] self.update_link(fib, new_slots, band_type) + return new_slots diff --git a/src/opticalcontroller/tools.py b/src/opticalcontroller/tools.py index 09cf3fbb4154590bd1047d5fae8feaccec531981..d953eb6f9b83d3b77a76587548c4695ac727181c 100644 --- a/src/opticalcontroller/tools.py +++ b/src/opticalcontroller/tools.py @@ -1,4 +1,4 @@ -# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,12 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json import numpy as np +from opticalcontroller.variables import * +import json , logging +from context.client.ContextClient import ContextClient from common.proto.context_pb2 import TopologyId , LinkId , OpticalLink , OpticalLinkDetails from common.tools.object_factory.OpticalLink import correct_slot -from context.client.ContextClient import ContextClient -from opticalcontroller.variables import * def common_slots(a, b): @@ -156,9 +156,7 @@ def get_side_slots_on_link(link, val, old_slots): starting_slot = keys[-1] num = 0 res = [] - print('starting_slot',starting_slot) - print('y',y) - + #print(starting_slot) for slot_id in range(starting_slot, len(y)): if link[y[slot_id]] == 1: num += 1 @@ -169,32 +167,6 @@ def get_side_slots_on_link(link, val, old_slots): return res, num -def get_side_slots_on_link_v2(link, val, old_slots): - #link = l["optical_details"][band] - x = list(old_slots.keys()) - y = list(link.keys()) - keys = str_list_to_int(x) - keys.sort() - - #print("AAAA") - #print(link, val, old_slots, keys) - #print(x) - starting_slot = keys[-1] - num = 0 - res = [] - print('starting_slot',starting_slot) - print('y',y) - - for slot_id in range(starting_slot, len(y)+1): - if link[str(slot_id)] == 1: - num += 1 - res.append(int(slot_id)) - - if num == val or slot_id == len(y): - return res, num - - - def frequency_converter(b, slots): l = len(slots) if debug: @@ -218,14 +190,16 @@ def frequency_converter(b, slots): def readTopologyData(nodes, topology): - nodes_file = open(nodes, 'r') - topo_file = open(topology, 'r') - nodes = json.load(nodes_file) - topo = json.load(topo_file) - #print(topo) - nodes_file.close() - topo_file.close() - return nodes, topo + + + nodes_file = open(nodes, 'r') + topo_file = open(topology, 'r') + nodes = json.load(nodes_file) + topo = json.load(topo_file) + #print(topo) + nodes_file.close() + topo_file.close() + return nodes, topo def readTopologyDataFromContext(topology_id:TopologyId): @@ -289,5 +263,69 @@ 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(link) + print(f"invoked from {test}") + print(f"fib updated {fib}") + optical_link = OpticalLink() + linkId = LinkId() + #linkId.link_uuid.uuid=link["link_id"]["link_uuid"]["uuid"] + linkId.link_uuid.uuid=link["link_id"]["link_uuid"]["uuid"] + optical_details = OpticalLinkDetails() + optical_link.optical_details.length=0 + if "src_port" in fib : + optical_link.optical_details.src_port=fib["src_port"] + if "dst_port" in fib : + optical_link.optical_details.dst_port=fib["dst_port"] + if "local_peer_port" in fib : + optical_link.optical_details.local_peer_port=fib['local_peer_port'] + if "remote_peer_port" in fib: + optical_link.optical_details.remote_peer_port=fib['remote_peer_port'] + + optical_link.optical_details.used=fib['used'] if 'used' in fib else False + if "c_slots" in fib : + + handle_slot( optical_link.optical_details.c_slots,fib["c_slots"]) + if "s_slots" in fib : + + handle_slot( optical_link.optical_details.s_slots,fib["s_slots"]) + if "l_slots" in fib : + + handle_slot( optical_link.optical_details.l_slots,fib["l_slots"]) + + + optical_link.name=link['name'] + + optical_link.link_id.CopyFrom(linkId) + + ctx_client = ContextClient() + ctx_client.connect() + try: + ctx_client.SetOpticalLink(optical_link) + except Exception as err: + print (f"setOpticalLink {err}") + diff --git a/src/service/service/ServiceServiceServicerImpl.py b/src/service/service/ServiceServiceServicerImpl.py index b101dab563eeefaf00fefa09c6dbc9da37288b6c..03a3db969e62867adcaa8ecde625b080384dcd11 100644 --- a/src/service/service/ServiceServiceServicerImpl.py +++ b/src/service/service/ServiceServiceServicerImpl.py @@ -44,7 +44,8 @@ from .task_scheduler.TaskScheduler import TasksScheduler from .tools.GeodesicDistance import gps_distance from .tools.OpticalTools import ( add_lightpath, delete_lightpath, adapt_reply, get_device_name_from_uuid, - get_optical_band, refresh_opticalcontroller, DelFlexLightpath + get_optical_band, refresh_opticalcontroller, DelFlexLightpath , extend_optical_band + ) @@ -304,8 +305,15 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): else: LOGGER.debug('expected optical band not found') elif reply_json["new_optical_band"]==2 : - pass - + parent_ob = reply_json["parent_opt_band"] + LOGGER.debug('Parent optical-band={}'.format(parent_ob)) + optical_band_txt = get_optical_band(parent_ob) + + + service_expansion= extend_optical_band(reply_json,optical_band_txt) + tasks_scheduler.compose_from_service_expansion(service_expansion) + + else: LOGGER.debug('Using existing optical band') else: diff --git a/src/service/service/service_handlers/oc/OCServiceHandler.py b/src/service/service/service_handlers/oc/OCServiceHandler.py index 51174ea7e5f33e2b60997abccda166e7ae819b3d..b636272946cc408f771ebd114c9ce986902afc98 100644 --- a/src/service/service/service_handlers/oc/OCServiceHandler.py +++ b/src/service/service/service_handlers/oc/OCServiceHandler.py @@ -25,7 +25,7 @@ from service.service.service_handler_api.SettingsHandler import SettingsHandler from service.service.task_scheduler.TaskExecutor import TaskExecutor from .ConfigRules import setup_config_rules, teardown_config_rules from .OCTools import ( - convert_endpoints_to_flows + endpoints_to_flows #handle_flows_names, check_media_channel_existance ) @@ -88,7 +88,7 @@ class OCServiceHandler(_ServiceHandler): is_opticalband =False #service_uuid = self.__service.service_id.service_uuid.uuid settings=None - + results = [] if self.__settings_handler.get('/settings-ob_{}'.format(connection_uuid)): is_opticalband=True settings = self.__settings_handler.get('/settings-ob_{}'.format(connection_uuid)) @@ -96,16 +96,24 @@ class OCServiceHandler(_ServiceHandler): settings = self.__settings_handler.get('/settings') bidir = settings.value.get("bidir") - LOGGER.debug(f"Bidir bvalue is: {bidir}") + LOGGER.debug(f"settings bvalue is: {settings}") # settings = self.__settings_handler.get('/settings') - - #flow is the new variable that stores input-output relationship - flows = convert_endpoints_to_flows(endpoints) + + # in case service expanded , the only part to reconfigure is optical band + ob_expansion =settings.value.get('ob-expanded',None) + if ob_expansion : + if not is_opticalband: + LOGGER.debug(f"ob-expanded bvalue is: {ob_expansion} and is_opticalband {is_opticalband}") + return results LOGGER.info(f"endpoints {endpoints} is_opticalband {is_opticalband} ") - #flows = endpoints_to_flows(endpoints, bidir, is_opticalband) - #handled_flows=handle_flows_names(flows=flows,task_executor=self.__task_executor) - results = [] + #flow is the new variable that stores input-output relationship + #flows = convert_endpoints_to_flows(endpoints) + flows = endpoints_to_flows(endpoints, bidir, is_opticalband) + LOGGER.info(f"endpoints {flows} is_opticalband {is_opticalband} ") + #handled_flows=handle_flows_names(flows=flows,task_executor=self.__task_executor) + + #new cycle for setting optical devices for device_uuid, dev_flows in flows.items(): @@ -138,7 +146,8 @@ class OCServiceHandler(_ServiceHandler): settings = self.__settings_handler.get('/settings-ob_{}'.format(connection_uuid)) else: settings = self.__settings_handler.get('/settings') - + + bidir = settings.value.get("bidir",None) results = [] for endpoint in endpoints: @@ -194,7 +203,8 @@ class OCServiceHandler(_ServiceHandler): if len(channel_indexes) > 0: errors = self.__task_executor.deconfigure_optical_device( device=device_obj, channel_indexes=channel_indexes, - is_opticalband=is_opticalband, dev_flow=dev_flows + is_opticalband=is_opticalband, dev_flow=dev_flows, + bidir=bidir ) # if (len(errors)==0): # service_id =self.__service.service_id diff --git a/src/service/service/service_handlers/oc/OCTools.py b/src/service/service/service_handlers/oc/OCTools.py index c0b400aac2a74f4428ad3ec3696a44323e5c0035..6f3b2af1ab391ea67ae1592391003e4783475846 100644 --- a/src/service/service/service_handlers/oc/OCTools.py +++ b/src/service/service/service_handlers/oc/OCTools.py @@ -22,6 +22,101 @@ log = logging.getLogger(__name__) #def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]])->Dict[str: List[Tuple[str, str]]]: +# def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]])->Dict: +# #entries = List[Tuple[str, str, str, Optional[str]]] +# #entries = Dict[str: List[Tuple[str, str]]] +# entries = {} +# #tuple is in, out +# #end = len(endpoints) if isinstance(endpoints,list) else 0 +# end = len(endpoints) +# i = 0 +# bidir = 0 +# log.debug("end={}".format(end)) +# while(i < end): +# endpoint = endpoints[i] +# device_uuid, endpoint_uuid = endpoint[0:2] +# log.info("current OCTools step {}, {}, {}".format(i, device_uuid, endpoint_uuid)) +# if device_uuid not in entries.keys(): +# entries[device_uuid] = [] +# if i == 0: +# entry_tuple = "0", endpoint_uuid +# entries[device_uuid].append(entry_tuple) +# next_endpoint = endpoints[i+1] +# next_device_uuid, next_endpoint_uuid = next_endpoint[0:2] +# if next_device_uuid == device_uuid: +# bidir = 1 +# log.info("connection is bidirectional") +# entry_tuple = next_endpoint_uuid, "0" +# entries[device_uuid].append(entry_tuple) +# i = i + 1 +# else: +# log.debug("connection is unidirectional") +# else: +# if not bidir: +# if i == end-1: +# #is the last node +# entry_tuple = endpoint_uuid, "0" +# entries[device_uuid].append(entry_tuple) +# else: +# #it is a transit node +# next_endpoint = endpoints[i+1] +# next_device_uuid, next_endpoint_uuid = next_endpoint[0:2] +# if next_device_uuid == device_uuid: +# entry_tuple = endpoint_uuid, next_endpoint_uuid +# entries[device_uuid].append(entry_tuple) +# i = i + 1 +# log.info("current OCTools step {}, {}, {}".format(i, next_device_uuid, device_uuid)) +# else: +# log.debug("ERROR in unidirectional connection 4") +# return {} +# else: +# log.debug("Ocheck i {}, {}, {}".format(i, i+1, end-1)) +# if i + 1 == end-1: +# log.debug("current OCTools step {}, {}, {}".format(i, device_uuid, endpoint_uuid)) +# #is the last node +# entry_tuple = endpoint_uuid, "0" +# entries[device_uuid].append(entry_tuple) +# next_endpoint = endpoints[i+1] +# log.debug("OCTools i+1 step {}, {}, {}".format(i+1, next_device_uuid, device_uuid)) + +# next_device_uuid, next_endpoint_uuid = next_endpoint[0:2] +# if next_device_uuid == device_uuid: +# entry_tuple = "0", next_endpoint_uuid +# entries[device_uuid].append(entry_tuple) +# i = i + 1 +# else: +# log.debug("ERROR in bidirectional connection 2") +# return entries +# else: +# log.debug("OCTools i+1+2+3 step {}, {}, {}".format(i+1, next_device_uuid, device_uuid)) +# #i+1 +# next_endpoint = endpoints[i+1] +# next_device_uuid, next_endpoint_uuid = next_endpoint[0:2] +# if next_device_uuid == device_uuid: +# entry_tuple = endpoint_uuid, next_endpoint_uuid +# entries[device_uuid].append(entry_tuple) +# else: +# log.debug("ERROR in bidirectional connection 3") +# log.debug("{}, {}, {}".format(i, next_device_uuid, device_uuid)) +# return entries +# #i+2 +# next_2_endpoint = endpoints[i+2] +# next_2_device_uuid, next_2_endpoint_uuid = next_2_endpoint[0:2] +# #i+3 +# next_3_endpoint = endpoints[i+3] +# next_3_device_uuid, next_3_endpoint_uuid = next_3_endpoint[0:2] +# if next_2_device_uuid == next_3_device_uuid and next_3_device_uuid == device_uuid: +# entry_tuple = next_2_endpoint_uuid, next_3_endpoint_uuid +# entries[device_uuid].append(entry_tuple) +# i = i + 3 +# else: +# log.debug("ERROR in bidirection connection 4") +# return {} +# i = i + 1 +# return entries + + + def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]])->Dict: #entries = List[Tuple[str, str, str, Optional[str]]] #entries = Dict[str: List[Tuple[str, str]]] @@ -105,6 +200,8 @@ def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]]) #i+3 next_3_endpoint = endpoints[i+3] next_3_device_uuid, next_3_endpoint_uuid = next_3_endpoint[0:2] + log.debug(f"de {device_uuid} and i {i}") + log.debug(f"de2 {next_2_device_uuid} and dev3 {next_3_device_uuid}") if next_2_device_uuid == next_3_device_uuid and next_3_device_uuid == device_uuid: entry_tuple = next_2_endpoint_uuid, next_3_endpoint_uuid entries[device_uuid].append(entry_tuple) @@ -116,6 +213,8 @@ def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]]) return entries + + def ob_flows(endpoints : List[Tuple[str, str, Optional[str]]], bidir : int): entries = {} end = len(endpoints) @@ -239,6 +338,7 @@ def conn_flows(endpoints : List[Tuple[str, str, Optional[str]]], bidir : int): i = i + 1 #if bidir reading 4 endpoints per node if bidir: + log.info(f"i starts with {i} ") i = i + 1 while(i < end-2): #i @@ -263,6 +363,8 @@ def conn_flows(endpoints : List[Tuple[str, str, Optional[str]]], bidir : int): #i+3 next_3_endpoint = endpoints[i+3] next_3_device_uuid, next_3_endpoint_uuid = next_3_endpoint[0:2] + log.info(f"dev {device_uuid} ") + log.info(f"dev2 {next_2_device_uuid} dev3 {next_3_device_uuid} ") if next_2_device_uuid == next_3_device_uuid and next_3_device_uuid == device_uuid: entry_tuple = next_2_endpoint_uuid, next_3_endpoint_uuid entries[device_uuid].append(entry_tuple) diff --git a/src/service/service/task_scheduler/TaskExecutor.py b/src/service/service/task_scheduler/TaskExecutor.py index 8e65f2578ffcade850458d63330e0957f4df2208..df0ebe31dfad5e09af21f8b27fdf1307e04afd1e 100644 --- a/src/service/service/task_scheduler/TaskExecutor.py +++ b/src/service/service/task_scheduler/TaskExecutor.py @@ -161,7 +161,11 @@ class TaskExecutor: # Deconfiguring Optical Devices ( CNIT ) def deconfigure_optical_device( - self, device : Device, channel_indexes : list, is_opticalband : bool, dev_flow : list + self, device : Device + , channel_indexes : list + , is_opticalband : bool + , dev_flow : list + ,bidir=None ): errors = [] flows = [] @@ -186,6 +190,7 @@ class TaskExecutor: # for extractor in device service to extract the index , dummy data for freq and band required indexes["frequency"] = None indexes["band"] = None + indexes["bidir"]=bidir if result is not None: new_config = json.loads(result.config) new_config["new_config"]=indexes diff --git a/src/service/service/task_scheduler/TaskScheduler.py b/src/service/service/task_scheduler/TaskScheduler.py index 9017149d653a6508d3f3dfeeb61955249bf14ab4..7f47f65fba761a3e947143511a9464f7a91821b7 100644 --- a/src/service/service/task_scheduler/TaskScheduler.py +++ b/src/service/service/task_scheduler/TaskScheduler.py @@ -13,9 +13,11 @@ # limitations under the License. import graphlib, logging, queue, time +from common.method_wrappers.ServiceExceptions import NotFoundException from typing import TYPE_CHECKING, Dict, Tuple from common.proto.context_pb2 import ( - Connection, ConnectionId, Service, ServiceId, ServiceStatusEnum, ConnectionList + Connection, ConnectionId, Service, ServiceId, ServiceStatusEnum, ConnectionList, + OpticalBand ) from common.proto.pathcomp_pb2 import PathCompReply from common.tools.grpc.Tools import grpc_message_to_json_string @@ -305,6 +307,67 @@ class TasksScheduler: t1 = time.time() LOGGER.debug('[compose_from_service] elapsed_time: {:f} sec'.format(t1-t0)) + + + + def compose_from_service_expansion( + self, service :Service, + ) -> None: + t0 = time.time() + include_service = self._optical_service_create + include_connection = self._optical_connection_configure + + logging.debug(f"after setting the config {service}") + #pending_items_to_explore.put(service) + has_media_channel = None + has_optical_band = None + if service is None : raise NotFoundException('Service', service, extra_details=[ + 'service not found ' + ]) + + + + connections = self._context_client.ListConnections(service.service_id) + has_media_channel, has_optical_band = self.check_service_for_media_channel( + connections=connections, item=service.service_id + ) + + _,service_key_done= include_service(service.service_id , + has_media_channel=has_media_channel, + has_optical_band=has_optical_band) + # self._add_service_to_executor_cache(service) + service_updating_key = self._add_task_if_not_exists(Task_ServiceSetStatus( + self._executor, service.service_id, ServiceStatusEnum.SERVICESTATUS_UPDATING + )) + self._add_service_to_executor_cache(service) + for connection in connections.connections: + connection_key = include_connection( + connection.connection_id, connection.service_id, has_media_channel=has_media_channel, + has_optical_band=has_optical_band + ) + self._add_connection_to_executor_cache(connection) + self._dag.add(connection_key, service_updating_key) + + + # for connection in pathcomp_reply.connections: + + # connection_key = include_connection( + # connection.connection_id, connection.service_id, has_media_channel=has_media_channel, + # has_optical_band=has_optical_band + # ) + # self._add_connection_to_executor_cache(connection) + + # self._executor.get_service(connection.service_id) + # for sub_service_id in connection.sub_service_ids: + # _,service_key_done = include_service( + # sub_service_id, has_media_channel=has_media_channel, + # has_optical_band=has_optical_band + # ) + # self._executor.get_service(sub_service_id) + # self._dag.add(connection_key, service_key_done) + + t1 = time.time() + LOGGER.debug('[compose_from_service] elapsed_time: {:f} sec'.format(t1-t0)) def compose_from_optical_service(self, service : Service, params:dict, is_delete : bool = False) -> None: t0 = time.time() diff --git a/src/service/service/tools/OpticalTools.py b/src/service/service/tools/OpticalTools.py index 20f0ffa294935c89eaa52a4fc67cbdb8dbdaa923..ab7b6fbce959d8b565126855fc0b2493fe9d08da 100644 --- a/src/service/service/tools/OpticalTools.py +++ b/src/service/service/tools/OpticalTools.py @@ -13,12 +13,16 @@ # limitations under the License. # +from common.method_wrappers.ServiceExceptions import NotFoundException +from service.service.service_handler_api.SettingsHandler import SettingsHandler import functools, json, logging, requests, uuid from typing import List -from common.Constants import ServiceNameEnum +from context.client.ContextClient import ContextClient +from common.Constants import ServiceNameEnum +from common.tools.context_queries.OpticalConfig import ( find_optical_band) from common.proto.context_pb2 import( Device, DeviceId, Service, Connection, EndPointId, TopologyId, ContextId, Uuid, - ConfigRule, ConfigActionEnum, ConfigRule_Custom + ConfigRule, ConfigActionEnum, ConfigRule_Custom, Empty,OpticalBandId,OpticalBand ) from common.proto.pathcomp_pb2 import PathCompReply from common.Settings import ( @@ -113,7 +117,7 @@ def refresh_opticalcontroller(topology_id : dict): urlx = "{:s}/GetTopology/{:s}/{:s}".format(base_url, cxt_id_str, topo_id_str) res = requests.get(urlx, headers=headers) if res is not None: - log.debug(f"DELETELIGHTPATH Response {res}") + log.debug(f"GetTopology Response {res}") def add_lightpath(src, dst, bitrate, bidir, ob_band) -> str: @@ -216,7 +220,7 @@ def adapt_reply(devices, service, reply_json, context_id, topology_id, optical_b uuuid_x = str(uuid.uuid4()) connection_ob.connection_id.connection_uuid.uuid = uuuid_x connection_ob.service_id.CopyFrom(service.service_id) - + new_ob = r["new_optical_band"] if 'new_optical_band' in r else None ob_id = ob["optical_band_id"] obt = ob["band_type"] if obt == "l_slots": @@ -242,49 +246,51 @@ def adapt_reply(devices, service, reply_json, context_id, topology_id, optical_b } rules_ob.append(ConfigRule_Custom(resource_key="/settings-ob_{}".format(uuuid_x), resource_value=json.dumps(val_ob))) bidir_ob = ob["bidir"] - for devxb in ob["flows"].keys(): - log.debug("optical-band device {}".format(devxb)) - in_end_point_b = "0" - out_end_point_b = "0" - in_end_point_f = ob["flows"][devxb]["f"]["in"] - out_end_point_f = ob["flows"][devxb]["f"]["out"] - log.debug("optical-band ports {}, {}".format(in_end_point_f, out_end_point_f)) - if bidir_ob: - in_end_point_b = ob["flows"][devxb]["b"]["in"] - out_end_point_b = ob["flows"][devxb]["b"]["out"] - log.debug("optical-band ports {}, {}".format(in_end_point_b, out_end_point_b)) - #if (in_end_point_f == "0" or out_end_point_f == "0") and (in_end_point_b == "0" or out_end_point_b == "0"): - if in_end_point_f != "0": - d_ob, p_ob = get_uuids_from_names(devices, devxb, in_end_point_f) - if d_ob != "" and p_ob != "": - end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) - connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) - else: - log.info("no map device port for device {} port {}".format(devxb, in_end_point_f)) - - if out_end_point_f != "0": - d_ob, p_ob = get_uuids_from_names(devices, devxb, out_end_point_f) - if d_ob != "" and p_ob != "": - end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) - connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) - else: - log.info("no map device port for device {} port {}".format(devxb, out_end_point_f)) - if in_end_point_b != "0": - d_ob, p_ob = get_uuids_from_names(devices, devxb, in_end_point_b) - if d_ob != "" and p_ob != "": - end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) - connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) - else: - log.info("no map device port for device {} port {}".format(devxb, in_end_point_b)) - if out_end_point_b != "0": - d_ob, p_ob = get_uuids_from_names(devices, devxb, out_end_point_b) - if d_ob != "" and p_ob != "": - end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) - connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) - else: - log.info("no map device port for device {} port {}".format(devxb, out_end_point_b)) - log.debug("optical-band connection {}".format(connection_ob)) - + # in case the service is built upon existed optical band , don't clacluate the endpoints of it + if new_ob != 2 : + for devxb in ob["flows"].keys(): + log.debug("optical-band device {}".format(devxb)) + in_end_point_b = "0" + out_end_point_b = "0" + in_end_point_f = ob["flows"][devxb]["f"]["in"] + out_end_point_f = ob["flows"][devxb]["f"]["out"] + log.debug("optical-band ports {}, {}".format(in_end_point_f, out_end_point_f)) + if bidir_ob: + in_end_point_b = ob["flows"][devxb]["b"]["in"] + out_end_point_b = ob["flows"][devxb]["b"]["out"] + log.debug("optical-band ports {}, {}".format(in_end_point_b, out_end_point_b)) + #if (in_end_point_f == "0" or out_end_point_f == "0") and (in_end_point_b == "0" or out_end_point_b == "0"): + if in_end_point_f != "0": + d_ob, p_ob = get_uuids_from_names(devices, devxb, in_end_point_f) + if d_ob != "" and p_ob != "": + end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) + connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) + else: + log.info("no map device port for device {} port {}".format(devxb, in_end_point_f)) + + if out_end_point_f != "0": + d_ob, p_ob = get_uuids_from_names(devices, devxb, out_end_point_f) + if d_ob != "" and p_ob != "": + end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) + connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) + else: + log.info("no map device port for device {} port {}".format(devxb, out_end_point_f)) + if in_end_point_b != "0": + d_ob, p_ob = get_uuids_from_names(devices, devxb, in_end_point_b) + if d_ob != "" and p_ob != "": + end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) + connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) + else: + log.info("no map device port for device {} port {}".format(devxb, in_end_point_b)) + if out_end_point_b != "0": + d_ob, p_ob = get_uuids_from_names(devices, devxb, out_end_point_b) + if d_ob != "" and p_ob != "": + end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) + connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) + else: + log.info("no map device port for device {} port {}".format(devxb, out_end_point_b)) + log.debug("optical-band connection {}".format(connection_ob)) + connection_f = add_connection_to_reply(opt_reply) connection_f.connection_id.connection_uuid.uuid = str(uuid.uuid4()) connection_f.service_id.CopyFrom(service.service_id) @@ -356,9 +362,10 @@ def adapt_reply(devices, service, reply_json, context_id, topology_id, optical_b service.service_config.config_rules.add().CopyFrom(rule) if len(rules_ob) > 0: - for rulex in rules_ob: - rule_ob = ConfigRule(action=ConfigActionEnum.CONFIGACTION_SET, custom=rulex) - service.service_config.config_rules.add().CopyFrom(rule_ob) + if new_ob != 2 : + for rulex in rules_ob: + rule_ob = ConfigRule(action=ConfigActionEnum.CONFIGACTION_SET, custom=rulex) + service.service_config.config_rules.add().CopyFrom(rule_ob) opt_reply.services.add().CopyFrom(service) return opt_reply @@ -371,3 +378,64 @@ def add_service_to_reply(reply : PathCompReply, service : Service) -> Service: def add_connection_to_reply(reply : PathCompReply) -> Connection: conn = reply.connections.add() return conn + + + + +def update_config_rules (service:Service,config_to_update:dict): + config_rules = service.service_config.config_rules + if len(config_rules) == 0 : return service + for key,new_value in config_to_update.items(): + for c in config_rules: + if c.custom.resource_key == key : + c.custom.resource_value = json.dumps(new_value) + + + return service + + + + +def extend_optical_band (reply,optical_band_text)->Service : + logging.debug(f"optical-band extended {reply}") + logging.debug(f"optical-band_text {optical_band_text}") + optical_band_res= json.loads(optical_band_text) + if 'optical_band_id' not in optical_band_res: raise KeyError(f"opticalband id not found in the reply") + ob_index =optical_band_res['optical_band_id'] + band=optical_band_res['band'] + frequency=optical_band_res['freq'] + opticalband=find_optical_band(ob_index=ob_index) + if opticalband is None : + raise NotFoundException(f"Optical Band is not found ",extra_details=[ + f"The requested opticla band for index {ob_index} is not found" + ]) + + service = opticalband.service + connection_uuid = opticalband.connection_id.connection_uuid.uuid + + setting_handler = SettingsHandler(service.service_config) + config_to_update = {} + setting_key = '/settings-ob_{}'.format(connection_uuid) + config = setting_handler.get(setting_key) + + + config.value['band']=band + config.value['frequency']=frequency + config.value['low-freq']= int(frequency - (band/2)) + config.value['up-freq']= int(frequency + (band/2)) + + logging.debug(f"before setting the config {service}") + config_to_update[setting_key]=config.value + setting_key = '/settings' + config = setting_handler.get(setting_key) + config.value['ob-expanded']=1 + config_to_update[setting_key]=config.value + logging.debug(f"config_to_update {config_to_update}") + service = update_config_rules(service,config_to_update) + + + return service + + + + diff --git a/src/webui/requirements.in b/src/webui/requirements.in index e80851b4a2a89898cdfef36c292e23b62b9f1514..fe1333480603087952803f51f243b179faa29908 100644 --- a/src/webui/requirements.in +++ b/src/webui/requirements.in @@ -19,3 +19,4 @@ flask-unittest==0.1.3 lorem-text==2.1 werkzeug==2.3.7 requests==2.27.1 + diff --git a/src/webui/service/__main__.py b/src/webui/service/__main__.py index 44a8f2e015ac516484722acd1326a586c125bd54..6022e1d7225101cda7e90166a20b77306c547793 100644 --- a/src/webui/service/__main__.py +++ b/src/webui/service/__main__.py @@ -14,7 +14,9 @@ import hashlib, sys, logging from prometheus_client import start_http_server +from datetime import timedelta from common.Constants import ServiceNameEnum + from common.Settings import ( ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name, get_log_level, get_metrics_port, get_service_baseurl_http, get_service_port_http, get_setting, wait_for_environment_variables @@ -27,6 +29,7 @@ def create_unique_session_cookie_name() -> str: if hostname is None: return 'session' hasher = hashlib.blake2b(digest_size=8) hasher.update(hostname.encode('UTF-8')) + logging.info('session:{:s}'.format(str(hasher.hexdigest()))) return 'session:{:s}'.format(str(hasher.hexdigest())) def main(): @@ -63,7 +66,9 @@ def main(): 'SECRET_KEY': SECRET_KEY, 'MAX_CONTENT_LENGTH': MAX_CONTENT_LENGTH, 'SESSION_COOKIE_NAME': create_unique_session_cookie_name(), - 'WTF_CSRF_ENABLED':False + 'WTF_CSRF_ENABLED':False, + + }, web_app_root=web_app_root) #app = create_app(use_config=None, web_app_root=web_app_root) app.run(host=host, port=service_port, debug=debug) diff --git a/src/webui/service/device/routes.py b/src/webui/service/device/routes.py index f3896bdd8f80ddcbcc55f91707caa5e462801cab..eb86f9ef4b99ba11c0c68b73c32026cb3056a334 100644 --- a/src/webui/service/device/routes.py +++ b/src/webui/service/device/routes.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json +import json ,logging , time from flask import current_app, render_template, Blueprint, flash, session, redirect, url_for from common.DeviceTypes import DeviceTypeEnum from common.proto.context_pb2 import ( @@ -29,7 +29,9 @@ device_client = DeviceClient() @device.get('/') def home(): + logging.info(f'session device {session}') if 'context_uuid' not in session or 'topology_uuid' not in session: + flash("Please select a context!", "warning") return redirect(url_for("main.home")) diff --git a/src/webui/service/main/routes.py b/src/webui/service/main/routes.py index c3e4fe1a3ed6b47622733f8541f065c268fce547..19cf116d4d1602e5b8811597d2e2244dba8e43d6 100644 --- a/src/webui/service/main/routes.py +++ b/src/webui/service/main/routes.py @@ -53,11 +53,13 @@ def process_descriptors(descriptors): @main.route('/', methods=['GET', 'POST']) def home(): + + LOGGER.info(f'session {session}') context_client.connect() device_client.connect() context_topology_form = ContextTopologyForm() context_topology_form.context_topology.choices.append(('', 'Select...')) - logging.info(f"CSRF Token in Session: {session}") + contexts : ContextList = context_client.ListContexts(Empty()) for context_ in contexts.contexts: #context_uuid : str = context_.context_id.context_uuid.uuid @@ -74,8 +76,7 @@ def home(): context_topology_form.context_topology.choices.append(context_topology_entry) if context_topology_form.validate_on_submit(): - logging.info(f"CSRF Token from Form: {request.form.get('csrf_token')}") - logging.info(f"CSRF Token in Session: {session.get('csrf_token')}") + context_topology_uuid = context_topology_form.context_topology.data if len(context_topology_uuid) > 0: b64_values = context_topology_uuid.split(',') @@ -186,10 +187,10 @@ def about(): def debug(): return render_template('main/debug.html') -@main.get('/resetsession') -def reset_session(): - session.clear() - return redirect(url_for("main.home")) +# @main.get('/resetsession') +# def reset_session(): +# session.clear() +# return redirect(url_for("main.home")) @main.get('/add_all') diff --git a/src/webui/service/optical_link/routes.py b/src/webui/service/optical_link/routes.py index 8a46a9124de2baf5bc3974fda8c41fdd4c666aa4..e7c20e6e0c2c85a1384523e7953bdb3e941a3f2f 100644 --- a/src/webui/service/optical_link/routes.py +++ b/src/webui/service/optical_link/routes.py @@ -14,12 +14,13 @@ from flask import current_app, render_template, Blueprint, flash, session, redirect, url_for -from common.proto.context_pb2 import Empty, OpticalLink, LinkId, OpticalLinkList +from common.proto.context_pb2 import Empty, OpticalLink, LinkId, OpticalLinkList , Service from common.tools.context_queries.EndPoint import get_endpoint_names from common.tools.context_queries.Topology import get_topology from context.client.ContextClient import ContextClient import functools ,requests ,logging from common.Constants import ServiceNameEnum +from common.tools.context_queries.OpticalConfig import find_optical_band from common.Settings import ( ENVVAR_SUFIX_SERVICE_BASEURL_HTTP, ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, find_environment_variables, get_env_var_name @@ -170,12 +171,21 @@ def get_optical_bands(): headers = {"Content-Type": "application/json"} optical_bands ={} + service_uuid = None try: r = requests.get(urlx, headers=headers) reply = r.json() - if (reply):optical_bands=reply + if (reply): + optical_bands=reply + ob_keys = optical_bands.keys() + for ob_key in ob_keys : + ob_service = find_optical_band(ob_key) + if ob_service is not None : + service_uuid=ob_service.service.service_id.service_uuid.uuid + optical_bands[ob_key]['service_uuid']=service_uuid - logging.info(f"optical bands {reply}") + logging.info(f"optical bands {optical_bands}") + except Exception as e : logging.info(f"error {e}") finally: diff --git a/src/webui/service/opticalconfig/routes.py b/src/webui/service/opticalconfig/routes.py index 298ef77a938d81f1a49572e8e90e822790c63ce8..7203c035cd9ee5778b571106db532330488a6fe9 100644 --- a/src/webui/service/opticalconfig/routes.py +++ b/src/webui/service/opticalconfig/routes.py @@ -12,15 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json, logging +import json, logging , time from flask import ( request, redirect, render_template, Blueprint, flash, session, url_for, current_app, make_response ) from common.proto.context_pb2 import ( - Empty, OpticalConfig, OpticalConfigId, OpticalConfigList + Empty, OpticalConfig, OpticalConfigId, OpticalConfigList , TopologyId ) +from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.context_queries.OpticalConfig import opticalconfig_get_uuid +from common.tools.object_factory.Context import json_context_id +from common.tools.object_factory.Topology import json_topology_id from common.DeviceTypes import DeviceTypeEnum from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient @@ -43,10 +46,11 @@ DESCRIPTOR_LOADER_NUM_WORKERS = 10 def home() : list_config = [] if 'context_uuid' not in session or 'topology_uuid' not in session: + #time.sleep(5) flash("Please select a context!", "warning") + return redirect(url_for("main.home")) - context_uuid = session['context_uuid'] - topology_uuid = session['topology_uuid'] + context_client.connect() opticalConfig_list : OpticalConfigList = context_client.GetOpticalConfig(Empty()) @@ -154,6 +158,57 @@ def delete_opitcalconfig (opticalconfig_uuid) : return redirect(url_for('opticalconfig.home')) +@opticalconfig.route('delete_all', methods=['GET']) +def delete_all_devices () : + try : + context_uuid = session['context_uuid'] + topology_uuid = session['topology_uuid'] + json_topo_id = json_topology_id(topology_uuid, context_id=json_context_id(context_uuid)) + context_client.connect() + response = context_client.GetTopologyDetails(TopologyId(**json_topo_id)) + context_client.close() + + devices = [] + for device in response.devices: + devices.append({ + 'device_id': device.device_id, + 'name': device.name, + 'type': device.device_type, + }) + + + optical_links = [] + for link in response.optical_links: + if len(link.link_endpoint_ids) != 2: + str_link = grpc_message_to_json_string(link) + LOGGER.warning('Unexpected link with len(endpoints) != 2: {:s}'.format(str_link)) + continue + optical_links.append({ + 'id': link.link_id.link_uuid.uuid, + 'name': link.name, + 'source': link.link_endpoint_ids[0].device_id.device_uuid.uuid, + 'target': link.link_endpoint_ids[1].device_id.device_uuid.uuid, + }) + if len(optical_links)!= 0 : + flash(f'you should delete all optical links before deleting all the devices ', 'danger') + return redirect(url_for('opticalconfig.home')) + if len(devices) > 0 : + LOGGER.info(f'devices to delete {devices}') + device_client.connect() + for d in devices: + if 'device_id' in d : + device_client.DeleteDevice(d['device_id']) + + flash('All devices were deleted successfully','success') + + + except Exception as e: # pylint: disable=broad-except + flash(f'Problem deleting optical devices ', 'danger') + current_app.logger.exception(e) + return redirect(url_for('opticalconfig.home')) + + + @opticalconfig.route('/update_opticalconfig', methods=['POST']) def update_externally(): if (request.method == 'POST'): diff --git a/src/webui/service/service/routes.py b/src/webui/service/service/routes.py index 02062e98198c98948b4967b4a2a4df5cde9b1ce5..e70d902cbccf2c5632060da0cbac7b4f3bf242e7 100644 --- a/src/webui/service/service/routes.py +++ b/src/webui/service/service/routes.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import grpc, json, logging #, re +import grpc, json, logging , time #, re from collections import defaultdict from flask import current_app, redirect, render_template, Blueprint, flash, session, url_for, request from typing import Optional, Set @@ -69,7 +69,9 @@ def get_device_drivers_in_use(topology_uuid: str, context_uuid: str) -> Set[str] @service.get('/') def home(): + LOGGER.info(f'session service {session}') if 'context_uuid' not in session or 'topology_uuid' not in session: + flash("Please select a context!", "warning") return redirect(url_for("main.home")) context_uuid = session['context_uuid'] @@ -270,6 +272,7 @@ def add_xr(): @service.get('<path:service_uuid>/detail') def detail(service_uuid: str): if 'context_uuid' not in session or 'topology_uuid' not in session: + flash("Please select a context!", "warning") return redirect(url_for("main.home")) context_uuid = session['context_uuid'] diff --git a/src/webui/service/templates/opticalconfig/home.html b/src/webui/service/templates/opticalconfig/home.html index 6a6437270e4622684c58c1e59fa1ab760a3e4f46..df1d906bc3535a53cb8842f34de55571cbc6b2b0 100644 --- a/src/webui/service/templates/opticalconfig/home.html +++ b/src/webui/service/templates/opticalconfig/home.html @@ -41,6 +41,13 @@ Refresh </button> </div> + <div class="col-sm-3"> + <!-- <button type="button" class="btn btn-danger"><i class="bi bi-x-square"></i>Delete link</button> --> + <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteModal"> + <i class="bi bi-x-square"></i> + Delete All Optical Devices + </button> + </div> </div> </div> @@ -109,5 +116,26 @@ </form> </div> --> </div> + + <div class="modal fade" id="deleteModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" + aria-labelledby="staticBackdropLabel" aria-hidden="true"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="staticBackdropLabel">Delete All Optical Devices?</h5> + <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> + </div> + <div class="modal-body"> + Are you sure you want to delete all the devices ? + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">No</button> + <a type="button" class="btn btn-danger" + href="{{ url_for('opticalconfig.delete_all_devices') }}"><i + class="bi bi-exclamation-diamond"></i>Yes</a> + </div> + </div> + </div> +</div> {% endblock %} diff --git a/src/webui/service/templates/opticalconfig/opticalbands.html b/src/webui/service/templates/opticalconfig/opticalbands.html index 7719512725c2eba4a87521968cee2d5f471bf353..1c5259ca343ce0ae53825facf2a12da482574e97 100644 --- a/src/webui/service/templates/opticalconfig/opticalbands.html +++ b/src/webui/service/templates/opticalconfig/opticalbands.html @@ -50,6 +50,22 @@ <tbody> {% for k, v in field_value.items() %} + + {% if k == 'service_uuid' %} + <tr> + + <td>Related Service </td> + <td> + {{v}} + <a href="{{ url_for('service.detail', service_uuid=v) }}" style="margin: 0px 6px;"> + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16"> + <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/> + <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/> + </svg> + </a> + </td> + </tr> + {%else %} <tr> <td> @@ -59,10 +75,9 @@ {{ v }} </td> </tr> - + {%endif%} {% endfor %} - </tbody> </table> diff --git a/test.py b/test.py index 2e64accf11582f674f75ff92bb45269baf24ea4f..c17ba3453c5fca5733675c94ecfea703054d24fb 100644 --- a/test.py +++ b/test.py @@ -27,14 +27,37 @@ media_channel = ''' ''' - +disable_mc= ''' +<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>2</index> + <config> + <index>2</index> + </config> + </channel> + </media-channels> + </wavelength-router> +</config> +''' +create_ob= ''' +<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> <name>C_BAND</name> +<index>1</index> <lower-frequency>192006252</lower-frequency> +<upper-frequency>192206250.0</upper-frequency> <admin-status>ENABLED</admin-status> +</config> <dest> <config> <port-name>101</port-name> +</config> </dest> </optical-band> </optical-bands> </wavelength-router></config> +''' # Define device connection details device = { 'host': '10.0.2.4', # replace with the target device's hostname or IP address - 'port': 2026, # NETCONF default port + 'port': 2025, # NETCONF default port 'username': 'admin', # replace with your username 'password': 'admin', # replace with your password 'hostkey_verify': False , # disable host key verification (use only for testing) @@ -56,7 +79,7 @@ with manager.connect(**device) as m: print("Device Configuration:") with open('d.log', 'w') as log_file: print(config,file=log_file) - # result = m.edit_config(target='running', config=media_channel) + # result = m.edit_config(target='running', config=create_ob) # print (result)