diff --git a/.gitignore b/.gitignore
index 7635bb0d2d784ada9a05abec91d2a6ec729a7ecd..5b7ac121a7512842cb158fbb3fc514c504beebfb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -192,4 +192,3 @@ libyang/
# Other logs
**/logs/*.log.*
-
diff --git a/proto/context.proto b/proto/context.proto
index ed194a87d5cf2d2cdc786811bb0e7482333ce121..9fde6a3c132843eeab8e921ad49aedb3f2cd18fb 100644
--- a/proto/context.proto
+++ b/proto/context.proto
@@ -251,6 +251,7 @@ enum DeviceDriverEnum {
DEVICEDRIVER_MORPHEUS = 17;
DEVICEDRIVER_RYU = 18;
DEVICEDRIVER_GNMI_NOKIA_SRLINUX = 19;
+ DEVICEDRIVER_OPENROADM = 20;
}
enum DeviceOperationalStatusEnum {
diff --git a/src/common/DeviceTypes.py b/src/common/DeviceTypes.py
index 008cb448d1cc849dddf67bf314e0dccec0cd81ae..1be1085536f68c527d52dd074bc422150397933d 100644
--- a/src/common/DeviceTypes.py
+++ b/src/common/DeviceTypes.py
@@ -30,6 +30,7 @@ class DeviceTypeEnum(Enum):
EMULATED_OPEN_LINE_SYSTEM = 'emu-open-line-system'
EMULATED_OPTICAL_ROADM = 'emu-optical-roadm'
EMULATED_OPTICAL_TRANSPONDER = 'emu-optical-transponder'
+ EMULATED_OPEN_ROADM = 'emu-optical-openroadm'
EMULATED_OPTICAL_SPLITTER = 'emu-optical-splitter' # passive component required for XR Constellation
EMULATED_P4_SWITCH = 'emu-p4-switch'
EMULATED_PACKET_RADIO_ROUTER = 'emu-packet-radio-router'
diff --git a/src/common/tools/context_queries/OpticalConfig.py b/src/common/tools/context_queries/OpticalConfig.py
index 23d1244cd60a505a329af522509c69f3f5559738..3e8a5380e7f1d46639a0890e43e89fe27f9ecd2d 100644
--- a/src/common/tools/context_queries/OpticalConfig.py
+++ b/src/common/tools/context_queries/OpticalConfig.py
@@ -67,6 +67,18 @@ def opticalconfig_get_uuid(
('name', device_name),
], extra_details=['At least one is required to produce a OpticalConfig UUID'])
+def opticalconfig_uuid_get_duuid(
+ device_uuid , allow_random : bool = False
+) -> str:
+
+ if (len(device_uuid)>0):
+ return get_uuid_from_string(f"{device_uuid}_opticalconfig")
+ if allow_random: return get_uuid_random()
+
+ raise InvalidArgumentsException([
+ ('DeviceId ', device_id),
+ ], extra_details=['device_id is required to produce a OpticalConfig UUID'])
+
def ob_get_uuid(
ob_name:str , allow_random : bool = False
diff --git a/src/context/service/database/OpticalConfig.py b/src/context/service/database/OpticalConfig.py
index 24a669f04fba3356e5d789fc5a9ccfd9b5f40034..7f6942d046e2ec477422d3450625bd370a3baf08 100644
--- a/src/context/service/database/OpticalConfig.py
+++ b/src/context/service/database/OpticalConfig.py
@@ -77,8 +77,7 @@ 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 'interfaces' in config and len(config['interfaces']) > 0:
for interface in config['interfaces']:
interface_name=interface["name"] if "name" in interface else None
@@ -86,24 +85,24 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig):
"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,
+ "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
+ "last_clear" : interface["last_clear" ] if "last_clear" in interface else None
})
if 'channels' in config and len(config['channels']) > 0:
@@ -178,7 +177,7 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig):
if 'interfaces' in config:
for interface in config['interfaces']:
interfaces.append({
- "interface_uuid" : interface_get_uuid(interface['name']),
+ "interface_uuid" : interface_get_uuid(interface['name'],device_uuid),
'name' : interface["name"],
"type" : interface["type"],
"administrative_state": interface["administrative_state"],
@@ -186,7 +185,27 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig):
"port" : interface["port"],
"interface_list" : interface["interface_list"],
"frequency" : interface["frequency"],
- "width" : interface["width"],
+ "width" : int(float(interface["width"])) if 'width' in interface else 0,
+ "roadm_uuid" : roadm_get_uuid(device_id),
+ })
+ if 'circuits' in config:
+ for interface in config['circuits']:
+ ports_g=''
+ if 'port' in interface :
+ for p in interface['port']:
+ if ports_g != '': ports_g +='/'
+ ports_g= ports_g+ p['port_name']
+
+ interfaces.append({
+ "interface_uuid" : interface_get_uuid(interface["interface_uuid"],device_uuid),
+ 'name' : interface["interface_list"] if 'interface_list' in interface else interface["interface_uuid"],
+ "type" : "Null",
+ "administrative_state": interface["administrative_state"] if 'administrative_state' in interface else "not-in-service",
+ "circuit_pack_name" : interface["circuit_pack_name"],
+ "port" : ports_g,
+ "interface_list" : interface["interface_list"] if 'interface_list' in interface else "Null",
+ "frequency" : interface["frequency"] if 'frequency' in interface else 0.0,
+ "width" : int(float(interface["width"])) if 'width' in interface else 0,
"roadm_uuid" : roadm_get_uuid(device_id),
})
roadms.append({
@@ -243,17 +262,17 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig):
)
stmt = stmt.returning(OpticalChannelModel.channel_uuid)
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' }
+ if (len(interfaces)>0) :
+ model_columns = inspect(TransponderInterfaceModel).c.keys()
+ stmt = insert(TransponderInterfaceModel).values(interfaces)
- )
- stmt = stmt.returning(TransponderInterfaceModel.interface_uuid)
- interface_id = session.execute(stmt).fetchone()
+ 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_:
@@ -299,24 +318,27 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig):
stmt = stmt.returning(RoadmTypeModel.roadm_uuid)
roadm_id = session.execute(stmt).fetchone()
- if len(interfaces) > 0:
- stmt = insert(ORInterfaceModel).values(interfaces)
- stmt = stmt.on_conflict_do_update(
- index_elements=[ORInterfaceModel.interface_uuid],
- set_=dict(
- name = stmt.excluded.name,
- frequency = stmt.excluded.frequency,
- administrative_state = stmt.excluded.administrative_state,
- type = stmt.excluded.type,
- circuit_pack_name = stmt.excluded.circuit_pack_name,
- port = stmt.excluded.port,
- interface_list = stmt.excluded.interface_list,
- width = stmt.excluded.width,
- )
- )
- stmt = stmt.returning(ORInterfaceModel.interface_uuid)
- opticalChannel_id = session.execute(stmt).fetchone()
-
+ if len(interfaces) > 0:
+ ifc_uuids=[]
+ for ifc in interfaces :
+ stmt = insert(ORInterfaceModel).values(**ifc)
+ update_stmt = stmt.on_conflict_do_update(
+ index_elements=[ORInterfaceModel.interface_uuid],
+ set_=dict(
+ name = stmt.excluded.name,
+ frequency = stmt.excluded.frequency,
+ administrative_state = stmt.excluded.administrative_state,
+ type = stmt.excluded.type,
+ circuit_pack_name = stmt.excluded.circuit_pack_name,
+ port = stmt.excluded.port,
+ interface_list = stmt.excluded.interface_list,
+ width = stmt.excluded.width,
+ )
+ ).returning(ORInterfaceModel.interface_uuid)
+ result = session.execute(update_stmt).fetchone()
+ ifc_uuids.append(result)
+ else :
+ session.query(ORInterfaceModel).delete()
opticalconfig_id = run_transaction(sessionmaker(bind=db_engine), callback)
return {'opticalconfig_uuid': opticalconfig_id}
@@ -335,7 +357,6 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig):
#is_transpondre = False
opticalconfig_uuid = opticalconfig_get_uuid(device_id)
is_optical_band=None
- LOGGER.info(f"update_opticalconfig {request}")
if request.config :
config = json.loads(request.config)
@@ -360,27 +381,27 @@ def update_opticalconfig(db_engine : Engine, request : OpticalConfig):
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
+ "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:
@@ -618,7 +639,7 @@ def delete_opticalchannel(db_engine : Engine, messagebroker : MessageBroker, req
opticalconfig_uuid = request.opticalconfig_id.opticalconfig_uuid
channels = []
config_type = None
-
+ logging.info(f"DeleteOpticalchannel {request.config}")
if "type" in config :
config_type= config["type"]
if 'new_config' in config:
@@ -640,6 +661,23 @@ def delete_opticalchannel(db_engine : Engine, messagebroker : MessageBroker, req
"target_output_power": None,
"status" : "DISABLED"
})
+
+ elif config_type == DeviceTypeEnum.OPEN_ROADM._value_:
+ if 'new_config' in config and 'interfaces' in config['new_config']:
+ for i in config['new_config']['interfaces']:
+ channels.append({
+ "interface_uuid" : interface_get_uuid(i['interface_name'],device_uuid),
+ 'name' : i["interface_name"],
+ "type" :'Null',
+ "administrative_state": "Null",
+ "circuit_pack_name" : 'Null',
+ "port" : "Null",
+ "interface_list" :'Null',
+ "frequency" : "Null",
+ "width" : 0,
+ "roadm_uuid" : roadm_get_uuid(device_id),
+ })
+
elif config_type == DeviceTypeEnum.OPTICAL_ROADM._value_:
channel_num = flow_id
@@ -668,7 +706,7 @@ def delete_opticalchannel(db_engine : Engine, messagebroker : MessageBroker, req
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 = []
@@ -692,6 +730,12 @@ def delete_opticalchannel(db_engine : Engine, messagebroker : MessageBroker, req
stmt = stmt.returning(OpticalChannelModel.channel_uuid)
opticalChannel_id = session.execute(stmt).fetchone()
all_suceed.append(True)
+ elif config_type == DeviceTypeEnum.OPEN_ROADM._value_:
+ if len(channels) > 0:
+ for i in channels :
+ num_deleted = session.query(ORInterfaceModel).filter_by(interface_uuid=i['interface_uuid']).delete()
+ all_suceed.append(num_deleted > 0)
+
return all_suceed
all_deleted = run_transaction(sessionmaker(bind=db_engine), callback)
diff --git a/src/context/service/database/models/OpticalConfig/RoadmModel.py b/src/context/service/database/models/OpticalConfig/RoadmModel.py
index fb48b3e4ea0a231007b0fb64621c9854d66b9fea..744a11e3450f49c9883b4f0e293669270f6ce3dd 100644
--- a/src/context/service/database/models/OpticalConfig/RoadmModel.py
+++ b/src/context/service/database/models/OpticalConfig/RoadmModel.py
@@ -79,7 +79,7 @@ class ORInterfaceModel (_Base):
__tablename__ = 'open_roadm_interface'
interface_uuid = Column(String, primary_key = True)
- name = Column(String, nullable = False, unique = True)
+ name = Column(String, nullable = False)
type = Column(String, nullable = True)
administrative_state = Column(String, nullable = True)
circuit_pack_name = Column(String, nullable = True)
diff --git a/src/context/service/database/models/enums/DeviceDriver.py b/src/context/service/database/models/enums/DeviceDriver.py
index f48b96fccfab0015aa2f403af228716e99241ff6..faa8ace3af721368e843fd95a604f029d5534371 100644
--- a/src/context/service/database/models/enums/DeviceDriver.py
+++ b/src/context/service/database/models/enums/DeviceDriver.py
@@ -42,6 +42,7 @@ class ORM_DeviceDriverEnum(enum.Enum):
MORPHEUS = DeviceDriverEnum.DEVICEDRIVER_MORPHEUS
RYU = DeviceDriverEnum.DEVICEDRIVER_RYU
GNMI_NOKIA_SRLINUX = DeviceDriverEnum.DEVICEDRIVER_GNMI_NOKIA_SRLINUX
+ OPENROADM = DeviceDriverEnum.DEVICEDRIVER_OPENROADM
grpc_to_enum__device_driver = functools.partial(
grpc_to_enum, DeviceDriverEnum, ORM_DeviceDriverEnum)
diff --git a/src/device/service/DeviceServiceServicerImpl.py b/src/device/service/DeviceServiceServicerImpl.py
index f6523a107e898087ccf0c274d81f6547667fd6bb..f52347a57851118f3c655cb084bebb9e62763216 100644
--- a/src/device/service/DeviceServiceServicerImpl.py
+++ b/src/device/service/DeviceServiceServicerImpl.py
@@ -69,6 +69,7 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
context_client = ContextClient()
device = get_device(context_client, device_uuid, rw_copy=True)
+
if device is None:
# not in context, create blank one to get UUID, and populate it below
device = Device()
@@ -126,7 +127,11 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
else:
t_pop_endpoints = None
- is_optical_device = request.device_drivers[0] == DeviceDriverEnum.DEVICEDRIVER_OC
+ OPTICAL_DRIVERS = {
+ DeviceDriverEnum.DEVICEDRIVER_OC,
+ DeviceDriverEnum.DEVICEDRIVER_OPENROADM,
+ }
+ is_optical_device = request.device_drivers[0] in OPTICAL_DRIVERS
if len(device.device_config.config_rules) == len(connection_config_rules) and not is_optical_device:
# created from request, populate config rules using driver
t7 = time.time()
diff --git a/src/device/service/OpenConfigServicer.py b/src/device/service/OpenConfigServicer.py
index 437f31924d8753f9b57c40e7e72e3034c0ac6136..e43739255fc1e200044e6371abd5d349a4d2c97f 100644
--- a/src/device/service/OpenConfigServicer.py
+++ b/src/device/service/OpenConfigServicer.py
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import grpc, logging, json
+import grpc, logging, json , traceback
from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method
from common.method_wrappers.ServiceExceptions import NotFoundException
from common.proto.context_pb2 import (
@@ -22,6 +22,7 @@ from common.proto.context_pb2 import (
from common.proto.device_pb2_grpc import DeviceServiceServicer
from common.tools.context_queries.Device import get_device
from common.tools.mutex_queues.MutexQueues import MutexQueues
+from common.DeviceTypes import DeviceTypeEnum
from context.client.ContextClient import ContextClient
from .driver_api._Driver import _Driver
from .driver_api.DriverInstanceCache import DriverInstanceCache, get_driver
@@ -96,24 +97,36 @@ class OpenConfigServicer(DeviceServiceServicer):
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)
+ results,new_config = driver.SetConfig(resources=resources,conditions=conditions)
- for result in results:
- if result is not None:
- is_all_good = False
- raise Exception(result)
+ errors = [r for r in results if r is not None]
+ if errors:
+ raise Exception(f"Driver errors: {errors}")
if is_all_good:
#driver.GetConfig(resource_keys=[])
config = json.loads(request.config)
+
handled_flow = next((i for i in resources if i['resource_key'] == 'handled_flow'), None)
if handled_flow is not None and len(handled_flow) > 0:
config['flow_handled'] = handled_flow['value']
+
+
+ if 'interfaces' in new_config and device.device_type == DeviceTypeEnum.OPEN_ROADM._value_:
+ LOGGER.info(f"with_new_config {new_config}")
+ config["interfaces"].extend(new_config['interfaces'])
+ LOGGER.info(f"updating_the_oc {request}")
request.config=json.dumps(config)
- context_client.UpdateOpticalConfig(request)
- context_client.close()
+ context_client.SetOpticalConfig(request)
+ else :
+ request.config=json.dumps(config)
+ context_client.UpdateOpticalConfig(request)
+
except Exception as e:
- LOGGER.info("error in configuring %s",e)
+ logging.error("Exception occurred:\n%s", traceback.format_exc())
+ finally :
+ context_client.close()
+
return Empty()
@safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
@@ -150,7 +163,7 @@ class OpenConfigServicer(DeviceServiceServicer):
resources : list[dict] = []
is_all_good = True
config = json.loads(request.config)
- LOGGER.info(f"from disable optical device {config}")
+ #LOGGER.info(f"from disable optical device {config}")
try:
context_client = ContextClient()
device = get_device(
@@ -163,7 +176,15 @@ class OpenConfigServicer(DeviceServiceServicer):
resources, conditions = extract_resources(config=config, device=device)
driver : _Driver = get_driver(self.driver_instance_cache, device)
- results = driver.DeleteConfig(resources=resources,conditions=conditions)
+ results,config_delete = driver.DeleteConfig(resources=resources,conditions=conditions)
+ if config_delete and 'interfaces' in config_delete:
+ if 'new_config'in config :
+ config["new_config"]=config_delete
+ else :
+ config['new_config'] ={}
+ config["new_config"]=config_delete
+
+ request.config=json.dumps(config)
for result in results:
if result is not None:
is_all_good = False
diff --git a/src/device/service/Tools.py b/src/device/service/Tools.py
index 153a2c105170302faada354a825caafc23cc9cc9..6b8eb4caefe916b38c37f9fed57a5eb789e1c53a 100644
--- a/src/device/service/Tools.py
+++ b/src/device/service/Tools.py
@@ -555,10 +555,10 @@ def update_endpoints(src_device : Device, dst_device : Device) -> None:
if len(src_topology_uuid) > 0: dst_topology_id.topology_uuid.uuid = src_topology_uuid
if len(src_context_uuid) > 0: dst_topology_id.context_id.context_uuid.uuid = src_context_uuid
-def get_edit_target(device : Device, is_opticalband : bool) -> str:
- if is_opticalband: return 'optical-band'
+def get_edit_target(device : Device, is_opticalband : bool,has_flow_id:bool) -> str:
+ if is_opticalband:return 'optical-band'
if device.device_type == DeviceTypeEnum.OPTICAL_ROADM._value_: return 'media-channel'
- if device.device_type == DeviceTypeEnum.OPEN_ROADM._value_: return 'network-media-channel'
+ if device.device_type == DeviceTypeEnum.OPEN_ROADM._value_: return 'or'
return 'optical-channel'
def is_key_existed(key : str, keys_dic = dict, key_name_to_use = None) -> dict:
@@ -576,7 +576,8 @@ def extract_resources(config : dict, device : Device) -> list[list[dict], dict]:
conditions = {}
resources : list[dict] = []
is_opticalband = config.get('is_opticalband', False)
- conditions['edit_type'] = get_edit_target(device, is_opticalband)
+ flow_id=is_key_existed('flow_id', keys_dic=config['new_config'], key_name_to_use='index')
+ conditions['edit_type'] = get_edit_target(device, is_opticalband,'value' in flow_id)
if device.device_type == DeviceTypeEnum.OPEN_ROADM._value_ :
ports_dic = is_key_existed('ports',keys_dic=config['new_config'])
@@ -636,19 +637,50 @@ def extract_resources(config : dict, device : Device) -> list[list[dict], dict]:
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:
- if 'frequency' in config['new_config'] and 'band' in config['new_config'] and conditions['edit_type'] == 'media-channel':
- if config['new_config']['frequency'] is not None and config['new_config']['band'] is not None:
- lower_frequency = int(int(config['new_config']['frequency']) - (int(config['new_config']['band'])/2)+1)
- upper_frequency = int(int(config['new_config']['frequency']) + (int(config['new_config']['band'])/2))
- resources.append(is_key_existed('flow_id', keys_dic=config['new_config'], key_name_to_use='index'))
- #resources.append({'resource_key':'index','value':config['new_config']['flow_id'] if 'flow_id' in config['new_config'] else None})
- else:
- lower_frequency = config['new_config']['low-freq'] if 'low-freq' in config['new_config'] else None
- upper_frequency = config['new_config']['up-freq' ] if 'up-freq' in config['new_config'] else None
- resources.append(is_key_existed('ob_id', keys_dic=config['new_config'], key_name_to_use='index'))
- #resources.append({'resource_key':'index','value':config['new_config']['ob_id'] if 'ob_id' in config['new_config'] else None})
- resources.append({'resource_key': 'lower-frequency', 'value': lower_frequency})
- resources.append({'resource_key': 'upper-frequency', 'value': upper_frequency})
+ if device.device_type == DeviceTypeEnum.OPEN_ROADM._value_ :
+ resources.append(is_key_existed('band', keys_dic=config['new_config'], key_name_to_use='band'))
+
+ for t_port in handled_flow:
+ for i in list(t_port):
+ if i :
+ t_circuit ={}
+ for c in config['interfaces'] :
+
+ ports_list = c["port"].split('/')
+
+ if i in ports_list :
+ # in case it has no type the targeted port has no configuration yet
+ if c['type'] =='Null' :
+ resources.append({'resource_key': 'interface_list'+'-'+i, 'value': c["interface_list"] })
+ resources.append({"resource_key":"supporting-circuit-pack-name"+'-'+i,"value":c["circuit_pack_name"]})
+ resources.append({"resource_key":"administrative_state"+'-'+i,"value":c["administrative_state"]})
+ resources.append({"resource_key":"frequency","value":c["frequency"]})
+ resources.append({'resource_key': 'interface_type'+'-'+i, 'value': "Null" })
+ # in case it has nmc type , extract only the interface name which may be used later in cross connection
+ elif c['type'] == 'nmc':
+ resources.append({'resource_key': 'interface_name'+'-'+i+'-'+'nmc', 'value': c["name"] })
+ resources.append({'resource_key': 'interface_list'+'-'+i, 'value': c["interface_list"] })
+ resources.append({'resource_key': 'interface_type'+'-'+i+'-'+'nmc', 'value': "nmc" })
+ elif c['type'] == 'srg':
+ resources.append({'resource_key': 'interface_name'+'-'+i+'-'+'srg', 'value': c["name"] })
+ resources.append({'resource_key': 'interface_list'+'-'+i, 'value': c["interface_list"] })
+ resources.append({'resource_key': 'interface_type'+'-'+i+'-'+'srg', 'value': "srg" })
+
+
+ if not is_opticalband:
+
+ if 'frequency' in config['new_config'] and 'band' in config['new_config'] and conditions['edit_type'] == 'media-channel':
+ if config['new_config']['frequency'] is not None and config['new_config']['band'] is not None:
+ lower_frequency = int(int(config['new_config']['frequency']) - (int(config['new_config']['band'])/2)+1)
+ upper_frequency = int(int(config['new_config']['frequency']) + (int(config['new_config']['band'])/2))
+ resources.append(flow_id)
+ #resources.append({'resource_key':'index','value':config['new_config']['flow_id'] if 'flow_id' in config['new_config'] else None})
+ else:
+ lower_frequency = config['new_config']['low-freq'] if 'low-freq' in config['new_config'] else None
+ upper_frequency = config['new_config']['up-freq' ] if 'up-freq' in config['new_config'] else None
+ resources.append(is_key_existed('ob_id', keys_dic=config['new_config'], key_name_to_use='index'))
+ #resources.append({'resource_key':'index','value':config['new_config']['ob_id'] if 'ob_id' in config['new_config'] else None})
+ resources.append({'resource_key': 'lower-frequency', 'value': lower_frequency})
+ resources.append({'resource_key': 'upper-frequency', 'value': upper_frequency})
return [resources, conditions]
diff --git a/src/device/service/driver_api/DriverInstanceCache.py b/src/device/service/driver_api/DriverInstanceCache.py
index 624e61fadc7580b67b914a901bdea33ffa02e023..d2528280932569ce5b08c25488077faa01ea5f5e 100644
--- a/src/device/service/driver_api/DriverInstanceCache.py
+++ b/src/device/service/driver_api/DriverInstanceCache.py
@@ -53,7 +53,7 @@ class DriverInstanceCache:
MSG = 'Driver({:s}) selected for device({:s}) with filter_fields({:s})...'
LOGGER.info(MSG.format(str(driver_class.__name__), str(device_uuid), str(filter_fields)))
- if driver_class.__name__ == "OCDriver":
+ if driver_class.__name__ == "OCDriver" or driver_class.__name__ == "OpenROADMDriver":
driver_instance : _Driver = driver_class(address, port, device_uuid=device_uuid, **settings)
else:
driver_instance : _Driver = driver_class(address, port, **settings)
diff --git a/src/device/service/drivers/__init__.py b/src/device/service/drivers/__init__.py
index e7eaef83328347dd5f66bc675d38e89bd230efd4..5768e23101bdf4a2e490aec7b6dab451553259ce 100644
--- a/src/device/service/drivers/__init__.py
+++ b/src/device/service/drivers/__init__.py
@@ -225,6 +225,20 @@ if LOAD_ALL_DEVICE_DRIVERS:
FilterFieldEnum.DRIVER : DeviceDriverEnum.DEVICEDRIVER_OC,
}
]))
+
+if LOAD_ALL_DEVICE_DRIVERS:
+ from .openroadm.OpenROADMDriver import OpenROADMDriver # pylint: disable=wrong-import-position
+ DRIVERS.append(
+ (OpenROADMDriver, [
+ {
+ # Real Packet Router, specifying OpenConfig Driver => use OpenConfigDriver
+ FilterFieldEnum.DEVICE_TYPE: [
+ DeviceTypeEnum.OPEN_ROADM,
+
+ ],
+ FilterFieldEnum.DRIVER : DeviceDriverEnum.DEVICEDRIVER_OPENROADM,
+ }
+ ]))
if LOAD_ALL_DEVICE_DRIVERS:
from .qkd.QKDDriver2 import QKDDriver # pylint: disable=wrong-import-position
diff --git a/src/device/service/drivers/oc_driver/OCDriver.py b/src/device/service/drivers/oc_driver/OCDriver.py
index c4020eb69173b7316514dd090005f1609ae3eeac..f7c093ba35e7039e4df120dfd178a58579bc10ba 100644
--- a/src/device/service/drivers/oc_driver/OCDriver.py
+++ b/src/device/service/drivers/oc_driver/OCDriver.py
@@ -36,8 +36,8 @@ from context.client.ContextClient import ContextClient
from common.proto.context_pb2 import OpticalConfig
from .templates.discovery_tool.transponders import transponder_values_extractor
from .templates.discovery_tool.roadms import roadm_values_extractor
-from .templates.discovery_tool.open_roadm import openroadm_values_extractor
-from .templates.VPN.openroadm import network_media_channel_handler
+
+
DEBUG_MODE = False
logging.getLogger('ncclient.manager').setLevel(logging.DEBUG if DEBUG_MODE else logging.WARNING)
@@ -154,8 +154,7 @@ def edit_config(
):
str_method = 'DeleteConfig' if delete else 'SetConfig'
results = []
-
-
+ new_config = {}
str_config_messages=[]
if netconf_handler.vendor is None :
if str_method == 'SetConfig':
@@ -165,10 +164,6 @@ def edit_config(
elif (conditions['edit_type']=='optical-band'):
#roadm optical-band
str_config_messages = create_optical_band(resources)
- elif (conditions['edit_type']=='network-media-channel'):
- commit_per_rule=True
- #openroadm network media channel
- str_config_messages = network_media_channel_handler(resources)
else :
#roadm media-channel
str_config_messages=create_media_channel(resources)
@@ -185,9 +180,7 @@ def edit_config(
str_config_messages=delete_optical_band(resources)
else :
str_config_messages=disable_media_channel(resources)
-
- logging.info(f"edit_template : {str_config_messages}")
-
+
for str_config_message in str_config_messages:
# configuration of the received templates
if str_config_message is None: raise UnsupportedResourceKeyException("CONFIG")
@@ -196,9 +189,7 @@ def edit_config(
test_option=test_option, error_option=error_option, format=format)
if commit_per_rule:
netconf_handler.commit() # configuration commit
-
- #results[i] = True
- results.append(result)
+ results.append(result)
else :
if netconf_handler.vendor == "CISCO":
@@ -240,19 +231,23 @@ def edit_config(
chk_length(str_resource_name, resource, min_length=2, max_length=2)
resource_key,resource_value = resource
chk_string(str_resource_name + '.key', resource_key, allow_empty=False)
- str_config_messages = compose_config( # get template for configuration
- resource_key, resource_value, delete=delete, vendor=netconf_handler.vendor, message_renderer=netconf_handler.message_renderer)
- for str_config_message in str_config_messages: # configuration of the received templates
+ str_config_messages = compose_config(
+ resource_key,
+ resource_value,
+ delete=delete,
+ vendor=netconf_handler.vendor,
+ message_renderer=netconf_handler.message_renderer)
+ for str_config_message in str_config_messages:
if str_config_message is None: raise UnsupportedResourceKeyException(resource_key)
logger.debug('[{:s}] str_config_message[{:d}] = {:s}'.format(
str_method, len(str_config_message), str(str_config_message)))
- netconf_handler.edit_config( # configure the device
+ netconf_handler.edit_config(
config=str_config_message, target=target, default_operation=default_operation,
test_option=test_option, error_option=error_option, format=format)
if commit_per_rule:
- netconf_handler.commit() # configuration commit
+ netconf_handler.commit()
if 'table_connections' in resource_key:
- time.sleep(5) # CPU usage might exceed critical level after route redistribution, BGP daemon needs time to reload
+ time.sleep(5)
#results[i] = True
results.append(True)
@@ -272,7 +267,7 @@ def edit_config(
logger.exception(msg.format(str_method, str_operation, str(resources)))
results = [e for _ in resources] # if commit fails, set exception in each resource
- return results
+ return [results,new_config]
class OCDriver(_Driver):
def __init__(self, address : str, port : int,device_uuid=None, **settings) -> None:
@@ -330,6 +325,7 @@ class OCDriver(_Driver):
oc_values["type"] = self.__type
try:
xml_data = self.__netconf_handler.get().data_xml
+
if self.__type == "optical-transponder":
extracted_values = transponder_values_extractor(
data_xml=xml_data, resource_keys=transponder_filter_fields, dic=config
@@ -341,10 +337,7 @@ class OCDriver(_Driver):
oc_values["channel_namespace"] = channel_namespace
oc_values["endpoints" ] = endpoints
oc_values["ports" ] = ports_result
- elif (self.__type =='openroadm'):
- extracted_values=openroadm_values_extractor(data_xml=xml_data, resource_keys=[], dic=oc_values)
- ports_result = extracted_values[1]
- oc_values['interfaces'] = extracted_values[0]['interfaces']
+
else:
extracted_values = roadm_values_extractor(data_xml=xml_data, resource_keys=[], dic=config)
ports_result = extracted_values[0]
@@ -386,6 +379,7 @@ class OCDriver(_Driver):
results = edit_config(
self.__netconf_handler, self.__logger, resources, conditions=conditions
)
+ logging.info(f"results { results}")
return results
@metered_subclass_method(METRICS_POOL)
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 07968a9715abadc657cacde4d7d5bdda3fb00cf5..55d404f8dc3d55dc84468ad809e22a2853413672 100644
--- a/src/device/service/drivers/oc_driver/templates/VPN/common.py
+++ b/src/device/service/drivers/oc_driver/templates/VPN/common.py
@@ -55,3 +55,9 @@ def filter_config(resources:list,unwanted_keys=[])->list[list,dict,str]:
ports = extract_ports(resources=resources)
logging.info(f"filter_config {ports}")
return [config,ports,index]
+
+
+def convert_Thz (freq_hz:int)->int :
+ freq_thz = freq_hz / 1_000_000
+ freq_thz_rounded = round(freq_thz, 2)
+ return freq_thz_rounded
diff --git a/src/device/service/drivers/oc_driver/templates/VPN/openroadm.py b/src/device/service/drivers/oc_driver/templates/VPN/openroadm.py
deleted file mode 100644
index bbbdb36d9459e35706c212a037b524f950079e3d..0000000000000000000000000000000000000000
--- a/src/device/service/drivers/oc_driver/templates/VPN/openroadm.py
+++ /dev/null
@@ -1,187 +0,0 @@
-# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-
-from yattag import Doc, indent
-import logging
-from .common import seperate_port_config ,filter_config
-from decimal import Decimal
-
-
-
-create_mc_err= '''
-
-
-
- MC-TTP-DEG2-RX-193.3
- Media-Channel-TTP-193.3THz-degree-2-in
- openROADM-if:mediaChannelTrailTerminationPoint
- inService
- DEG2-AMPRX
- DEG2-AMPRX-IN
- OMS-DEG2-TTP-RX
-
- 193.25
- 193.35
-
-
-
- MC-TTP-DEG2-TX-193.3
- Media-Channel-TTP-193.3THz-degree-2-out
- openROADM-if:mediaChannelTrailTerminationPoint
- inService
- DEG2-AMPTX
- DEG2-AMPTX-OUT
- OMS-DEG2-TTP-TX
-
- 193.25
- 193.35
-
-
-
-
-
-
-
-'''
-
-def define_interface_name (type:str,interface_list:str,freq:int)->str:
- interface_str = interface_list.split('-')
- port_rank=''
- port_type=''
- if (len(interface_str)==4):
- port_rank=interface_str[1]
- port_type=interface_str[3]
- elif (len(interface_str)==5):
- port_rank=interface_str[2]
- port_type=interface_str[3]
- else :
- port_rank=interface_list
- port_type=interface_list+'type'
-
-
- return f'{type.upper()}-{port_rank}-{port_type}-{freq}'
-
-
-
-def create_media_channel (resources):
-
- frequency_dict=next((r for r in resources if r['resource_key']== 'frequency'),None)
- width_dict=next((r for r in resources if r['resource_key']== 'width'),None)
- interfaces_lists =next((r for r in resources if r['resource_key']== 'interfaces'),None)
- administrative_state= next((r for r in resources if r['resource_key']== 'administrative-state'),None)
- min_freq= int(Decimal(frequency_dict["value"])*1000) - (int(width_dict["value"])/2)
- max_freq = int(Decimal(frequency_dict["value"])*1000) + (int(width_dict["value"])/2)
- #config,_,_ = filter_config(resources=resources,unwanted_keys=unwanted_keys)
-
- or_device_ns="http://org/openroadm/device"
- or_interface_ns="http://org/openroadm/interfaces"
-
- results=[]
- logging.info(f"from openroadm mc {resources}")
- doc, tag, text = Doc().tagtext()
- with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"):
- with tag('org-openroadm-device', ('xmlns',or_device_ns)):
-
- for interface in interfaces_lists["value"]:
- port = next((r for r in interface if r['resource_key']=='supporting-port'),None)
- circuit_pack =next((r for r in interface if r['resource_key']=='supporting-circuit-pack-name'),None)
- interface_list = next((r for r in interface if r['resource_key']=='supporting-interface-list'),None)
- mc_name = define_interface_name('mc-ttp',interface_list['value'],frequency_dict['value'])
- interface.append({'resource_key':'mc_name','value':mc_name})
- with tag('interface'):
-
- with tag('name'):text(mc_name)
- with tag('description'):text(f'Media-channel-{frequency_dict["value"]}THz')
- with tag('type'):text("openROADM-if:mediaChannelTrailTerminationPoint")
- with tag('administrative-state'):text(administrative_state["value"])
- with tag('supporting-circuit-pack-name'):text(circuit_pack["value"])
- with tag('supporting-port'):text(port["value"])
- with tag('supporting-interface-list'):text(interface_list["value"])
- with tag('mc-ttp',xmlns="http://org/openroadm/media-channel-interfaces"):
- with tag('max-freq'):text(max_freq)
- with tag('min-freq'):text(min_freq)
-
-
-
- result = indent(
- doc.getvalue(),
- indentation = ' '*2,
- newline = ''
- )
- results.append(result)
- logging.info(f"from openroadm mc results {results}")
- return [results,resources]
-
-
-
-
-def create_network_media_channel (resources):
-
- logging.info(f"nmc resources {resources}")
-
- unwanted_keys= ['max-freq','min-freq']
- #config,_,_ = filter_config(resources=resources,unwanted_keys=unwanted_keys)
-
- or_device_ns="http://org/openroadm/device"
- frequency_dict=next((r for r in resources if r['resource_key']== 'frequency'),None)
- width_dict=next((r for r in resources if r['resource_key']== 'width'),None)
- interfaces_lists =next((r for r in resources if r['resource_key']== 'interfaces'),None)
- administrative_state= next((r for r in resources if r['resource_key']== 'administrative-state'),None)
-
-
- results=[]
- doc, tag, text = Doc().tagtext()
- with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"):
- with tag('org-openroadm-device', ('xmlns',or_device_ns)):
- for interface in interfaces_lists["value"]:
- port = next((r for r in interface if r['resource_key']=='supporting-port'),None)
- circuit_pack =next((r for r in interface if r['resource_key']=='supporting-circuit-pack-name'),None)
- interface_list = next((r for r in interface if r['resource_key']=='mc_name'),None)
- nmc_name = define_interface_name('nmc-ctp',interface_list['value'],frequency_dict['value'])
-
- with tag('interface'):
-
- with tag('name'):text(nmc_name)
- with tag('description'):text(f'Media-channel-{frequency_dict["value"]}THz')
- with tag('type'):text("openROADM-if:networkMediaChannelConnectionTerminationPoint")
- with tag('administrative-state'):text(administrative_state["value"])
- with tag('supporting-circuit-pack-name'):text(circuit_pack["value"])
- with tag('supporting-port'):text(port["value"])
- with tag('supporting-interface-list'):text(interface_list["value"])
- with tag('nmc-ctp',xmlns="http://org/openroadm/network-media-channel-interfaces"):
- with tag('frequency'):text(frequency_dict['value'])
- with tag('width'):text(width_dict['value'])
-
-
-
- result = indent(
- doc.getvalue(),
- indentation = ' '*2,
- newline = ''
- )
- results.append(result)
- logging.info(f"nmc message {results}")
- return results
-
-
-def network_media_channel_handler (resources):
- unwanted_keys=["config_type"]
- config,_,_ = filter_config(resources=resources,unwanted_keys=unwanted_keys)
- mc_list,resources_updated= create_media_channel(resources=config)
- nmc_list= create_network_media_channel(resources=resources_updated)
- mc_list.extend(nmc_list)
-
- return mc_list
diff --git a/src/device/service/drivers/openroadm/OpenROADMDriver.py b/src/device/service/drivers/openroadm/OpenROADMDriver.py
new file mode 100644
index 0000000000000000000000000000000000000000..f1a388b84b079d28fb03cd2fe0cccd940fecff3e
--- /dev/null
+++ b/src/device/service/drivers/openroadm/OpenROADMDriver.py
@@ -0,0 +1,304 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import time
+import json
+import logging, pytz, re, threading
+from typing import Any, List, Tuple, Union
+from apscheduler.executors.pool import ThreadPoolExecutor
+from apscheduler.jobstores.memory import MemoryJobStore
+from apscheduler.schedulers.background import BackgroundScheduler
+from ncclient.manager import Manager, connect_ssh
+from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method
+from common.tools.client.RetryDecorator import delay_exponential
+from common.type_checkers.Checkers import chk_length, chk_string, chk_type
+from device.service.driver_api.Exceptions import UnsupportedResourceKeyException
+from device.service.driver_api._Driver import _Driver
+
+
+from .RetryDecorator import retry
+from context.client.ContextClient import ContextClient
+from common.proto.context_pb2 import OpticalConfig
+
+from .templates.discovery_tool.open_roadm import openroadm_values_extractor
+from .templates.Provisioning.openroadm import (
+ or_handler
+ , srg_network_media_channel_handle
+ ,handle_or_deconfiguration
+ )
+
+DEBUG_MODE = False
+logging.getLogger('ncclient.manager').setLevel(logging.DEBUG if DEBUG_MODE else logging.WARNING)
+logging.getLogger('ncclient.transport.ssh').setLevel(logging.DEBUG if DEBUG_MODE else logging.WARNING)
+logging.getLogger('apscheduler.executors.default').setLevel(logging.INFO if DEBUG_MODE else logging.ERROR)
+logging.getLogger('apscheduler.scheduler').setLevel(logging.INFO if DEBUG_MODE else logging.ERROR)
+logging.getLogger('monitoring-client').setLevel(logging.INFO if DEBUG_MODE else logging.ERROR)
+
+
+
+SAMPLE_EVICTION_SECONDS = 30.0 # seconds
+SAMPLE_RESOURCE_KEY = 'interfaces/interface/state/counters'
+
+MAX_RETRIES = 15
+DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
+RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+context_client= ContextClient()
+
+
+class NetconfSessionHandler:
+ def __init__(self, address : str, port : int, **settings) -> None:
+ self.__lock = threading.RLock()
+ self.__connected = threading.Event()
+ self.__address = address
+ self.__port = int(port)
+ self.__username = settings.get('username')
+ self.__password = settings.get('password')
+ self.__vendor = settings.get('vendor',None)
+ self.__version = settings.get('version', "1")
+ self.__key_filename = settings.get('key_filename')
+ self.__hostkey_verify = settings.get('hostkey_verify', True)
+ self.__look_for_keys = settings.get('look_for_keys', True)
+ self.__allow_agent = settings.get('allow_agent', True)
+ self.__force_running = settings.get('force_running', False)
+ self.__commit_per_rule = settings.get('commit_per_rule', False)
+ self.__device_params = settings.get('device_params', {})
+ self.__manager_params = settings.get('manager_params', {})
+ self.__nc_params = settings.get('nc_params', {})
+ self.__message_renderer = settings.get('message_renderer','jinja')
+
+ self.__manager : Manager = None
+ self.__candidate_supported = False
+
+ def connect(self):
+ with self.__lock:
+ self.__manager = connect_ssh(
+ host=self.__address, port=self.__port, username=self.__username, password=self.__password,
+ device_params=self.__device_params, manager_params=self.__manager_params, nc_params=self.__nc_params,
+ key_filename=self.__key_filename, hostkey_verify=self.__hostkey_verify, allow_agent=self.__allow_agent,
+ look_for_keys=self.__look_for_keys)
+ self.__candidate_supported = ':candidate' in self.__manager.server_capabilities
+ self.__connected.set()
+
+ def disconnect(self):
+ if not self.__connected.is_set(): return
+ with self.__lock:
+ self.__manager.close_session()
+
+ @property
+ def use_candidate(self): return self.__candidate_supported and not self.__force_running
+
+ @property
+ def commit_per_rule(self): return self.__commit_per_rule
+
+ @property
+ def vendor(self): return self.__vendor
+
+ @property
+ def version(self): return self.__version
+
+ @property
+ def message_renderer(self): return self.__message_renderer
+
+ @RETRY_DECORATOR
+ def get(self, filter=None, with_defaults=None): # pylint: disable=redefined-builtin
+ with self.__lock:
+ config=self.__manager.get(filter=filter, with_defaults=with_defaults)
+ return config
+
+ @RETRY_DECORATOR
+ def edit_config(
+ self, config, target='running', default_operation=None, test_option=None,
+ error_option=None, format='xml' # pylint: disable=redefined-builtin
+ ):
+ response = None
+ with self.__lock:
+ response= self.__manager.edit_config(
+ config, target=target, default_operation=default_operation, test_option=test_option,
+ error_option=error_option, format=format)
+
+ str_respones = str(response)
+ if re.search(r'', str_respones):
+ return None
+ return str_respones
+
+ @RETRY_DECORATOR
+ def locked(self, target):
+ return self.__manager.locked(target=target)
+
+ @RETRY_DECORATOR
+ def commit(self, confirmed=False, timeout=None, persist=None, persist_id=None):
+ return self.__manager.commit(confirmed=confirmed, timeout=timeout, persist=persist, persist_id=persist_id)
+
+DRIVER_NAME = 'oc'
+METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': DRIVER_NAME})
+def edit_config( # edit the configuration of openconfig devices
+ netconf_handler : NetconfSessionHandler, logger : logging.Logger, resources : List[Tuple[str, Any]]
+ ,conditions, delete=False,
+ commit_per_rule=False, target='running', test_option=None, error_option=None,
+ format='xml'
+):
+ str_method = 'DeleteConfig' if delete else 'SetConfig'
+ results = []
+ new_config = {}
+ str_config_messages=[]
+ if netconf_handler.vendor is None :
+ if str_method == 'SetConfig':
+
+ if (conditions['edit_type']== 'or'):
+ str_config_messages,interfaces= or_handler(resources)
+ new_config['interfaces']= interfaces
+
+ #Disabling of the Configuration
+ else:
+ if (conditions['edit_type']=='or'):
+ str_config_messages,interfaces=handle_or_deconfiguration(resources)
+ new_config["interfaces"]=interfaces
+
+
+ for index,str_config_message in enumerate(str_config_messages):
+ # configuration of the received templates
+ if str_config_message is None: raise UnsupportedResourceKeyException("CONFIG")
+ result= netconf_handler.edit_config( # configure the device
+ config=str_config_message, target='running',
+ test_option=test_option, error_option=error_option, format=format)
+ if commit_per_rule:
+ if index == len(str_config_messages) - 1 :
+ netconf_handler.commit() # configuration commit
+
+ #results[i] = True
+ results.append(result)
+
+
+
+ return [results,new_config]
+
+class OpenROADMDriver(_Driver):
+ def __init__(self, address : str, port : int,device_uuid=None, **settings) -> None:
+ super().__init__(DRIVER_NAME, address, port, **settings)
+ self.__logger = logging.getLogger('{:s}:[{:s}:{:s}]'.format(str(__name__), str(self.address), str(self.port)))
+ self.__lock = threading.Lock()
+
+ self.__started = threading.Event()
+ self.__terminate = threading.Event()
+ self.__scheduler = BackgroundScheduler(daemon=True)
+ self.__scheduler.configure(
+ jobstores = {'default': MemoryJobStore()},
+ executors = {'default': ThreadPoolExecutor(max_workers=1)},
+ job_defaults = {'coalesce': False, 'max_instances': 3},
+ timezone=pytz.utc)
+ self._temp_address=f"{address}{port}"
+
+ self.__netconf_handler = NetconfSessionHandler(self.address, self.port, **(self.settings))
+ self.__type = self.settings.get("type","openroadm")
+ self.__device_uuid = device_uuid
+ self.Connect()
+
+ def Connect(self) -> bool:
+ with self.__lock:
+ if self.__started.is_set(): return True
+ self.__netconf_handler.connect()
+ self.__scheduler.start()
+ self.__started.set()
+ return True
+
+ def Disconnect(self) -> bool:
+ with self.__lock:
+ self.__terminate.set()
+ if not self.__started.is_set(): return True
+ self.__scheduler.shutdown()
+ self.__netconf_handler.disconnect()
+ return True
+
+ @metered_subclass_method(METRICS_POOL)
+ def GetInitialConfig(self) -> List[Tuple[str, Any]]:
+ with self.__lock:
+ return []
+
+ @metered_subclass_method(METRICS_POOL)
+ def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]:
+ chk_type('resources', resource_keys, list)
+ results = []
+ opticalConfig= OpticalConfig()
+
+ with self.__lock:
+ config = {}
+ transceivers = {}
+ oc_values = {}
+ ports_result = []
+ oc_values["type"] = self.__type
+ try:
+ xml_data = self.__netconf_handler.get().data_xml
+
+
+ if (self.__type =='openroadm'):
+ extracted_values=openroadm_values_extractor(data_xml=xml_data, resource_keys=[], dic=oc_values)
+ ports_result = extracted_values[1]
+ oc_values['interfaces'] = extracted_values[0]['interfaces']
+ oc_values ['circuits'] = extracted_values[0]['circuits']
+
+
+
+ #///////////////////////// store optical configurtaion ////////////////////////////////////////////////////////
+
+ opticalConfig.config=json.dumps(oc_values)
+ if self.__device_uuid is not None:
+ opticalConfig.device_id.device_uuid.uuid=self.__device_uuid
+ results.append((f"/opticalconfigs/opticalconfig/{self.__device_uuid}",{"opticalconfig":opticalConfig}))
+
+ except Exception as e: # pylint: disable=broad-except
+ MSG = 'Exception retrieving {:s}'
+ self.__logger.info("error from getConfig %s",e)
+ self.__logger.exception(MSG.format(e))
+
+ if len(ports_result) > 0: results.extend(ports_result)
+ return results
+
+ @metered_subclass_method(METRICS_POOL)
+ def SetConfig(self, resources : List[Tuple[str, Any]],conditions:dict) -> List[Union[bool, Exception]]:
+ if len(resources) == 0: return []
+ results = []
+ with self.__lock:
+ if self.__netconf_handler.use_candidate:
+ with self.__netconf_handler.locked(target='candidate'):
+ results = edit_config(
+ self.__netconf_handler, self.__logger, resources, conditions, target='candidate',
+ commit_per_rule=self.__netconf_handler.commit_per_rule
+ )
+ else:
+ results = edit_config(
+ self.__netconf_handler, self.__logger, resources, conditions=conditions
+ )
+ logging.info(f"results { results}")
+ return results
+
+ @metered_subclass_method(METRICS_POOL)
+ def DeleteConfig(
+ self, resources : List[Tuple[str, Any]], conditions : dict,
+
+ ) -> List[Union[bool, Exception]]:
+ chk_type('resources', resources, list)
+ if len(resources) == 0: return []
+
+ with self.__lock:
+ if self.__netconf_handler.use_candidate:
+ with self.__netconf_handler.locked(target='candidate'):
+ results = edit_config(
+ self.__netconf_handler, self.__logger, resources, target='candidate', delete=True,
+ commit_per_rule=self.__netconf_handler.commit_per_rule, conditions=conditions
+ )
+ else:
+ results = edit_config(
+ self.__netconf_handler, self.__logger, resources, delete=True, conditions=conditions
+ )
+ return results
diff --git a/src/device/service/drivers/openroadm/RetryDecorator.py b/src/device/service/drivers/openroadm/RetryDecorator.py
new file mode 100644
index 0000000000000000000000000000000000000000..df37414c15fa2be63ba74e6c6f9955749b15302a
--- /dev/null
+++ b/src/device/service/drivers/openroadm/RetryDecorator.py
@@ -0,0 +1,46 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging, time
+from common.tools.client.RetryDecorator import delay_linear
+
+LOGGER = logging.getLogger(__name__)
+
+def retry(max_retries=0, delay_function=delay_linear(initial=0, increment=0),
+ prepare_method_name=None, prepare_method_args=[], prepare_method_kwargs={}):
+ def _reconnect(func):
+ def wrapper(self, *args, **kwargs):
+ if prepare_method_name is not None:
+ prepare_method = getattr(self, prepare_method_name, None)
+ if prepare_method is None: raise Exception('Prepare Method ({}) not found'.format(prepare_method_name))
+ num_try, given_up = 0, False
+ while not given_up:
+ try:
+ return func(self, *args, **kwargs)
+ except OSError as e:
+ if str(e) != 'Socket is closed': raise
+
+ num_try += 1
+ given_up = num_try > max_retries
+ if given_up: raise Exception('Giving up... {:d} tries failed'.format(max_retries)) from e
+ if delay_function is not None:
+ delay = delay_function(num_try)
+ time.sleep(delay)
+ LOGGER.info('Retry {:d}/{:d} after {:f} seconds...'.format(num_try, max_retries, delay))
+ else:
+ LOGGER.info('Retry {:d}/{:d} immediate...'.format(num_try, max_retries))
+
+ if prepare_method_name is not None: prepare_method(*prepare_method_args, **prepare_method_kwargs)
+ return wrapper
+ return _reconnect
diff --git a/src/device/service/drivers/openroadm/Tools.py b/src/device/service/drivers/openroadm/Tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..45407d38d28892f9bbe049520cc3d615ec1bc965
--- /dev/null
+++ b/src/device/service/drivers/openroadm/Tools.py
@@ -0,0 +1,38 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import hashlib
+import uuid
+import xml.dom.minidom, xmltodict
+
+def xml_pretty_print(data : str):
+ return xml.dom.minidom.parseString(data).toprettyxml()
+
+def xml_to_file(data : str, file_path : str) -> None:
+ with open(file_path, mode='w', encoding='UTF-8') as f:
+ f.write(xml_pretty_print(data))
+
+def xml_to_dict(data : str):
+ return xmltodict.parse(data)
+
+def generate_uuid_from_numbers(code:str) ->str:
+ # Concatenate the numbers into a single string
+
+
+ # Generate a hash value using MD5 algorithm
+ hash_value = hashlib.md5(code.encode()).hexdigest()
+
+ # Convert the hash value into a UUID
+ generated_uuid = uuid.UUID(hash_value)
+
+ return str(generated_uuid)
diff --git a/src/device/service/drivers/openroadm/__init__.py b/src/device/service/drivers/openroadm/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460
--- /dev/null
+++ b/src/device/service/drivers/openroadm/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/device/service/drivers/openroadm/templates/Provisioning/__init__.py b/src/device/service/drivers/openroadm/templates/Provisioning/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460
--- /dev/null
+++ b/src/device/service/drivers/openroadm/templates/Provisioning/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/device/service/drivers/openroadm/templates/Provisioning/common.py b/src/device/service/drivers/openroadm/templates/Provisioning/common.py
new file mode 100644
index 0000000000000000000000000000000000000000..6edaefc2fd9339dc368cefd2ac8a49334ba0c5c5
--- /dev/null
+++ b/src/device/service/drivers/openroadm/templates/Provisioning/common.py
@@ -0,0 +1,63 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+
+
+def seperate_port_config(resources:list,unwanted_keys=[])->list[list,dict,str]:
+ config=[]
+ ports={}
+ index=None
+ for item in resources :
+ if len(unwanted_keys)>0:
+ if (item['value'] is not None and (item['resource_key'] not in unwanted_keys)):
+ config.append({'resource_key':item['resource_key'], 'value':item['value']} )
+ #if (item['resource_key'] == 'destination_port' or item['resource_key'] == 'source_port') and item['value'] is not None:
+ # ports[item['resource_key']]=item['value']
+ if (item['resource_key'] == 'destination_port' or item['resource_key'] == 'source_port'):
+ 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):
+ if len(resources) ==0 :return
+ ports=[]
+ flow=next((i for i in resources if i['resource_key']=='handled_flow'),None)
+ if flow is not None:
+ ports = flow['value']
+ return ports
+
+def filter_config(resources:list,unwanted_keys=[])->list[list,dict,str]:
+ config=[]
+ ports=()
+ index=None
+ for item in resources :
+ if len(unwanted_keys)>0:
+ if (item['value'] is not None and (item['resource_key'] not in unwanted_keys)):
+ config.append({'resource_key':item['resource_key'], 'value':item['value']} )
+ if (item['resource_key']=='index' and item['value'] is not None) :
+ index=item['value']
+ #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]
+
+
+def convert_Thz (freq_hz:int)->int :
+ freq_thz = freq_hz / 1_000_000
+ freq_thz_rounded = round(freq_thz, 2)
+ return freq_thz_rounded
diff --git a/src/device/service/drivers/openroadm/templates/Provisioning/openroadm.py b/src/device/service/drivers/openroadm/templates/Provisioning/openroadm.py
new file mode 100644
index 0000000000000000000000000000000000000000..32c03b026e98c75d529b747013219fc204eda285
--- /dev/null
+++ b/src/device/service/drivers/openroadm/templates/Provisioning/openroadm.py
@@ -0,0 +1,358 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+
+from yattag import Doc, indent
+import logging
+from .common import seperate_port_config ,filter_config ,convert_Thz
+from decimal import Decimal
+
+
+
+or_device_ns_1='urn:ietf:params:xml:ns:netconf:base:1.0'
+or_device_ns="http://org/openroadm/device"
+
+
+def define_interface_name_old (type:str,interface_list:str,freq:int)->str:
+
+ if interface_list :
+ interface_str = interface_list.split('-')
+ port_rank=''
+ port_type=''
+ if (len(interface_str)==4):
+ port_rank=interface_str[1]
+ port_type=interface_str[3]
+ elif (len(interface_str)==5):
+ port_rank=interface_str[2]
+ port_type=interface_str[3]
+ elif (len(interface_str)==2):
+ port_rank=interface_str[0]
+ port_type=interface_str[1]
+
+ else :
+ port_rank=interface_list
+ port_type=interface_list+'type'
+
+
+ return f'{type.upper()}-{port_rank}-{port_type}-{freq}'
+ return ''
+
+def define_interface_name (type:str,port:str,freq:int)->str:
+
+ return f'{type.upper()}-{port.upper()}-{freq}'
+
+
+
+def create_network_media_channel (resources,port,cross_conn_resources,interfaces):
+
+ band=next((r for r in resources if r['resource_key']== 'band'),None)
+ frequency_dict=next((r for r in resources if r['resource_key']== 'frequency'),None)
+ freq_thz=None
+ if frequency_dict and frequency_dict['value']:
+ freq_thz=frequency_dict['value']
+
+ doc, tag, text = Doc().tagtext()
+
+ if port:
+ with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"):
+ with tag('org-openroadm-device', ('xmlns',or_device_ns)):
+ #port,i_list,circuit=infer_opposite_port(i,interface_list["value"])
+ interface_list =next((r for r in resources if r['resource_key']== 'interface_list'+'-'+port),None)
+ circuit_pack =next((r for r in resources if r['resource_key']=='supporting-circuit-pack-name'+'-'+port),None)
+ nmc_name = define_interface_name(f'nmc',port,freq_thz)
+ cross_conn_resources[f'nmc_name_{port}']=nmc_name
+ cross_conn_resources[f'nmc_port_{port}']=port
+ cross_conn_resources['count']+=1
+ interfaces.append({
+ "name" : nmc_name,
+ "type" : 'nmc',
+ "administrative_state": 'inService',
+ "circuit_pack_name" : circuit_pack["value"],
+ "port" : port,
+ "interface_list" : interface_list["value"],
+ "frequency" : freq_thz,
+ "width" : band['value'],
+ "roadm_uuid" : ""
+ })
+
+ with tag('interface'):
+
+ with tag('name'):text(nmc_name)
+ with tag('description'):text(f'Media-channel-{freq_thz}THz')
+ with tag('type'):text("openROADM-if:networkMediaChannelConnectionTerminationPoint")
+ with tag('administrative-state'):text("inService")
+ with tag('supporting-circuit-pack-name'):text(circuit_pack['value'])
+ with tag('supporting-port'):text(port)
+ with tag('supporting-interface-list'):text(interface_list["value"])
+ with tag('nmc-ctp',xmlns="http://org/openroadm/network-media-channel-interfaces"):
+ with tag('frequency'):text(freq_thz)
+ with tag('width'):text(band['value'])
+
+ result = indent(
+ doc.getvalue(),
+ indentation = ' '*2,
+ newline = ''
+ )
+
+ return result
+
+
+def or_create_media_channel (resources,port,cross_conn_resources,interfaces):
+ frequency_dict=next((r for r in resources if r['resource_key']== 'frequency'),None)
+ freq_thz=None
+ if frequency_dict and frequency_dict['value']:
+ freq_thz=frequency_dict['value']
+ band=next((r for r in resources if r['resource_key']== 'band'),None)
+ min_freq= int(Decimal(frequency_dict["value"])) - (int(band["value"])/2)
+ max_freq =int(Decimal(frequency_dict["value"])) + (int(band["value"])/2)
+ results=[]
+ doc, tag, text = Doc().tagtext()
+
+ if port:
+ with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"):
+ with tag('org-openroadm-device', ('xmlns',or_device_ns)):
+
+ interface_list =next((r for r in resources if r['resource_key']== 'interface_list'+'-'+port),None)
+ circuit_pack =next((r for r in resources if r['resource_key']=='supporting-circuit-pack-name'+'-'+port),None)
+ mc_name = define_interface_name(f'mc-ttp',port,freq_thz)
+ interfaces.append({
+ "name" : mc_name,
+ "type" : 'mc',
+ "administrative_state" : 'inService',
+ "circuit_pack_name" : circuit_pack["value"],
+ "port" : port,
+ "interface_list" : interface_list["value"],
+ "frequency" : freq_thz,
+ "width" : band["value"],
+ "roadm_uuid" : ""
+ })
+
+ with tag('interface'):
+
+ with tag('name'):text(mc_name)
+ with tag('description'):text(f'Media-channel-{freq_thz}THz')
+ with tag('type'):text("openROADM-if:mediaChannelTrailTerminationPoint")
+ with tag('administrative-state'):text("inService")
+ with tag('supporting-circuit-pack-name'):text(circuit_pack["value"])
+ with tag('supporting-port'):text(port)
+ with tag('supporting-interface-list'):text(interface_list["value"])
+ with tag('mc-ttp',xmlns="http://org/openroadm/media-channel-interfaces"):
+ with tag('max-freq'):text(max_freq)
+ with tag('min-freq'):text(min_freq)
+ result = indent(
+ doc.getvalue(),
+ indentation = ' '*2,
+ newline = ''
+ )
+
+ results.append(result)
+ nmc_result = create_network_media_channel(resources,port,cross_conn_resources,interfaces)
+ results.append(nmc_result)
+ return results
+
+
+
+
+def create_cross_connection (resources):
+
+ src,dst = resources['ports']
+ connection_name=resources[f'nmc_name_{src}']+' to '+resources[f'nmc_name_{dst}']
+ doc, tag, text = Doc().tagtext()
+ with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"):
+ with tag('org-openroadm-device', ('xmlns',or_device_ns)):
+ with tag ('roadm-connections'):
+ with tag('connection-name'):text(connection_name)
+ with tag('opticalControlMode'):text('off')
+ with tag('target-output-power'):text('0')
+ with tag('source'):
+ with tag('src-if'):text(resources[f'nmc_name_{src}'])
+ with tag('destination') :
+ with tag('dst-if'):text(resources[f'nmc_name_{dst}'])
+
+ result = indent(
+ doc.getvalue(),
+ indentation = ' '*2,
+ newline = ''
+ )
+
+ # logging.info(f"nmc message {results}")
+ return result
+
+
+
+
+def srg_network_media_channel_handle (resources,port,cross_conn_resources,interfaces):
+
+
+ band=next((r for r in resources if r['resource_key']== 'band'),None)
+ frequency_dict=next((r for r in resources if r['resource_key']== 'frequency'),None)
+ freq_thz= frequency_dict["value"]
+ doc, tag, text = Doc().tagtext()
+ if port :
+ with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"):
+ with tag('org-openroadm-device', ('xmlns',or_device_ns)):
+
+ interface_list =next((r for r in resources if r['resource_key']== 'interface_list'+'-'+port),None)
+ circuit_pack =next((r for r in resources if r['resource_key']=='supporting-circuit-pack-name'+'-'+port),None)
+ srg_name = define_interface_name(f'nmc-srg',port,freq_thz)
+ cross_conn_resources[f'nmc_name_{port}']=srg_name
+ cross_conn_resources[f'nmc_port_{port}']=port
+ cross_conn_resources['count']+=1
+
+ interfaces.append({
+ "name" : srg_name,
+ "type" : 'srg',
+ "administrative_state" : 'inService',
+ "circuit_pack_name" : circuit_pack["value"],
+ "port" : port,
+ "interface_list" : interface_list["value"],
+ "frequency" : freq_thz,
+ "roadm_uuid" : ""
+ })
+
+ with tag('interface'):
+
+ with tag('name'):text(srg_name)
+ with tag('description'):text(f'Network-Media-Channel-CTP-{freq_thz}THz')
+ with tag('type'):text("openROADM-if:networkMediaChannelConnectionTerminationPoint")
+ with tag('administrative-state'):text("inService")
+ with tag('supporting-circuit-pack-name'):text(circuit_pack["value"])
+ with tag('supporting-port'):text(port)
+ with tag('nmc-ctp',xmlns="http://org/openroadm/network-media-channel-interfaces"):
+ with tag('frequency'):text(freq_thz)
+ with tag('width'):text(band['value'])
+ result = indent(
+ doc.getvalue(),
+ indentation = ' '*2,
+ newline = ''
+ )
+
+ return result
+
+def delete_interface (interface_name) :
+ doc, tag, text = Doc().tagtext()
+ with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"):
+ with tag('org-openroadm-device', xmlns=or_device_ns, **{'xmlns:nc': or_device_ns_1}):
+ with tag('interface', **{'nc:operation': 'delete'}):
+ with tag('name'):text(interface_name)
+
+ result = indent(
+ doc.getvalue(),
+ indentation = ' '*2,
+ newline = ''
+ )
+ return result
+
+def delete_coss_connection (resources) :
+
+ src,dst = resources['ports']
+ connection_name=resources[f'nmc_name_{src}']+' to '+resources[f'nmc_name_{dst}']
+ doc, tag, text = Doc().tagtext()
+ with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"):
+ with tag('org-openroadm-device', xmlns=or_device_ns, **{'xmlns:nc': or_device_ns_1}):
+ with tag ('roadm-connections', **{'nc:operation': 'delete'}):
+ with tag('connection-name'):text(connection_name)
+
+ result = indent(
+ doc.getvalue(),
+ indentation = ' '*2,
+ newline = ''
+ )
+
+ return result
+
+def or_handler (resources):
+
+ _,ports,_= filter_config(resources,unwanted_keys=[])
+ edit_templates= []
+ interfaces= []
+ cross_conn_resources={"count":0}
+ doc, tag, text = Doc().tagtext()
+ logging.info(f'or_handler {resources} ')
+ with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"):
+ with tag('org-openroadm-device', ('xmlns',or_device_ns)):
+ for lst_i in [list(j) for j in ports ] :
+ for i in lst_i:
+
+ if 'SRG' in i :
+ edit_templates.append (
+ srg_network_media_channel_handle(resources,i,cross_conn_resources,interfaces)
+ )
+
+ else :
+
+ edit_templates.extend(or_create_media_channel(resources ,i,cross_conn_resources,interfaces))
+
+ if cross_conn_resources['count']==2 :
+ cross_conn_resources['ports']=lst_i
+ edit_templates.append(create_cross_connection(cross_conn_resources))
+ cross_conn_resources["count"]=0
+ return (edit_templates,interfaces)
+
+
+def delete_mc (mc_interfaces):
+
+ results = []
+ for i in mc_interfaces:
+ results.append( delete_interface(i))
+ return results
+
+def handle_or_deconfiguration (resources):
+
+ _,ports,_= filter_config(resources,unwanted_keys=[])
+ edit_templates= []
+ interfaces= []
+ mcs_to_delete=[]
+ cross_conn_resources={"count":0}
+
+ for lst_i in [list(j) for j in ports ] :
+ for p in lst_i :
+ if p :
+ if 'SRG' in p :
+ interface_list = interface_list =next((r for r in resources if r['resource_key']== 'interface_name'+'-'+p+'-srg'),None)
+ if interface_list :
+ cross_conn_resources[f'nmc_name_{p}']=interface_list['value']
+ cross_conn_resources[f'nmc_port_{p}']=p
+ cross_conn_resources['count']+=1
+ interfaces.append({
+ 'interface_name':interface_list['value']
+ })
+
+ edit_templates.append(delete_interface(interface_list['value']))
+ else :
+ nmc_interface = interface_list =next((r for r in resources if r['resource_key']== 'interface_name'+'-'+p+'-nmc'),None)
+ if nmc_interface :
+ cross_conn_resources[f'nmc_name_{p}']=nmc_interface['value']
+ cross_conn_resources[f'nmc_port_{p}']=p
+ cross_conn_resources['count']+=1
+ mc_interface = nmc_interface['value'].replace('NMC',"MC-TTP")
+ interfaces.extend([{
+ 'interface_name':nmc_interface['value']
+ },
+ {
+ 'interface_name':mc_interface
+ }]
+
+ )
+ mcs_to_delete.append(mc_interface)
+ #delete_interface(mc_interface,doc,tag,text)
+ edit_templates.append(delete_interface(nmc_interface['value']))
+ if cross_conn_resources['count']==2 :
+ logging.info("should cross connection be initiated")
+ cross_conn_resources['ports']=lst_i
+ edit_templates.insert(0,delete_coss_connection(cross_conn_resources))
+ cross_conn_resources["count"]=0
+ edit_templates.extend(delete_mc(mcs_to_delete))
+ return (edit_templates,interfaces)
diff --git a/src/device/service/drivers/openroadm/templates/__init__.py b/src/device/service/drivers/openroadm/templates/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..7363515f07a52d996229bcbd72932ce1423258d7
--- /dev/null
+++ b/src/device/service/drivers/openroadm/templates/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/src/device/service/drivers/oc_driver/templates/discovery_tool/open_roadm.py b/src/device/service/drivers/openroadm/templates/discovery_tool/open_roadm.py
similarity index 72%
rename from src/device/service/drivers/oc_driver/templates/discovery_tool/open_roadm.py
rename to src/device/service/drivers/openroadm/templates/discovery_tool/open_roadm.py
index 9a75f020d1ecf3442d384e4b5ccdd88935e1a75a..39e089aeeee1eac0d3cb039edabeeb6dc6f7a831 100644
--- a/src/device/service/drivers/oc_driver/templates/discovery_tool/open_roadm.py
+++ b/src/device/service/drivers/openroadm/templates/discovery_tool/open_roadm.py
@@ -53,7 +53,7 @@ def extract_roadm_circuits_pack (xml_data:str):
port_info={}
port_name=port.find('./oc:port-name',namespace)
port_qual= port.find("./oc:port-qual",namespace)
-
+ #if port_qual is not None and port_qual.text != 'roadm-external':continue
if port_name is not None :
port_info["port_name"]=port_name.text
if port_qual is not None :
@@ -62,11 +62,12 @@ def extract_roadm_circuits_pack (xml_data:str):
# if port_info["port_qual"] == 'roadm-external':
# circuit_ports.append(port_info)
if (circuit_name is not None):
- circuit_info["circuit_name"]=circuit_name.text
+ circuit_info["circuit_pack_name"]=circuit_name.text
+ circuit_info["interface_uuid"]=circuit_name.text
if (circuit_type is not None):
circuit_info["circuit_type"]=circuit_type.text
if (circuit_adminstrative_status is not None):
- circuit_info["circuit_adminstrative_status"]=circuit_adminstrative_status.text
+ circuit_info["administrative_state"]=circuit_adminstrative_status.text
if (circuit_equipment_state is not None):
circuit_info["circuit_equipment_state"]=circuit_equipment_state.text
if (circuit_mode is not None):
@@ -74,14 +75,11 @@ def extract_roadm_circuits_pack (xml_data:str):
if (slot is not None):
circuit_info["slot"]=slot.text
if (shelf is not None):
- circuit_info["shelf"]=shelf.text
- logging.info(f"circuit_ports {circuit_ports}")
- circuit_info["ports"]=circuit_ports
-
+ circuit_info["shelf"]=shelf.text
+ circuit_info["port"]=circuit_ports
+ if len(circuit_info["port"])==0 : continue
circuits_list.append(circuit_info)
-
-
-
+
return circuits_list
@@ -137,14 +135,13 @@ def extract_openroadm_interface (xml_data:str):
interface_list =interface.find('.//oc:supporting-interface-list',namespace)
or_interfaces.append({
- 'interface_list':name.text if name is not None else None,
- 'administrative_state':administrative_state.text if administrative_state is not None else None,
- 'circuit_pack_name':circuit_pack_name.text if circuit_pack_name is not None else None,
- 'port':port.text if port is not None else None ,
- 'type':type.text if type is not None else None
+ 'interface_list' : name.text if name is not None else None,
+ 'administrative_state' : administrative_state.text if administrative_state is not None else None,
+ 'circuit_pack_name' : circuit_pack_name.text if circuit_pack_name is not None else None,
+ 'port' : port.text if port is not None else None ,
+ 'type' : type.text if type is not None else None
})
if mc is not None :
- print (mc)
frequency=None
width=None
min_frequency = mc.find('.//mc:min-freq',namespace)
@@ -155,15 +152,15 @@ def extract_openroadm_interface (xml_data:str):
mc= {
- 'name':name.text if name is not None else None,
- 'description':description.text if description is not None else None ,
- 'type':"media_channel",
- 'administrative_state':administrative_state.text if administrative_state is not None else None,
- 'circuit_pack_name':circuit_pack_name.text if circuit_pack_name is not None else None,
- 'port':port.text if port is not None else None ,
- 'interface_list': interface_list.text if interface_list is not None else None,
- 'frequency': str(frequency),
- 'width':width
+ 'name' : name.text if name is not None else None,
+ 'description' : description.text if description is not None else None ,
+ 'type' : "mc",
+ 'administrative_state': administrative_state.text if administrative_state is not None else None,
+ 'circuit_pack_name' : circuit_pack_name.text if circuit_pack_name is not None else None,
+ 'port' : port.text if port is not None else None ,
+ 'interface_list' : interface_list.text if interface_list is not None else None,
+ 'frequency' : str(frequency),
+ 'width' : width
}
or_config.append(mc)
@@ -175,18 +172,17 @@ def extract_openroadm_interface (xml_data:str):
frequency = nmc.find('.//nmc:frequency',namespace)
width=nmc.find('.//nmc:width',namespace)
nmc= {
- 'name':name.text if name is not None else None,
- 'description':description.text if description is not None else None ,
- 'type':"network_media_channel",
- 'administrative_state':administrative_state.text if administrative_state is not None else None,
- 'circuit_pack_name':circuit_pack_name.text if circuit_pack_name is not None else None,
- 'port':port.text if port is not None else None ,
- 'interface_list': interface_list.text if interface_list is not None else None,
- 'frequency': frequency.text if frequency is not None else None,
- 'width':width.text if width is not None else None
+ 'name' : name.text if name is not None else None,
+ 'description' : description.text if description is not None else None ,
+ 'type' : "nmc" if interface_list is not None else "srg",
+ 'administrative_state': administrative_state.text if administrative_state is not None else None,
+ 'circuit_pack_name' : circuit_pack_name.text if circuit_pack_name is not None else None,
+ 'port' : port.text if port is not None else None ,
+ 'interface_list' : interface_list.text if interface_list is not None else None,
+ 'frequency' : frequency.text if frequency is not None else None,
+ 'width' : width.text if width is not None else None
}
or_config.append(nmc)
- logging.info(f"or_config for or {or_config}")
return [or_interfaces,or_config]
@@ -200,22 +196,27 @@ def openroadm_values_extractor (data_xml:str,resource_keys:list,dic:dict):
dic['interfaces']=config
for circuit in circuits_list :
- circuit_name=circuit['circuit_name']
+ circuit_name=circuit['circuit_pack_name']
location = Location()
location.circuit_pack=circuit_name
- for port in circuit['ports']:
+ for port in circuit['port']:
if port is not None and 'port_name' in port :
resource_key = '/endpoints/endpoint[{:s}]'.format(port["port_name"])
- resource_value = {'uuid': port["port_name"]
- , 'type':port["port_qual"] if "port_qual" in port else None,
- 'location':{"circuit_pack":location.circuit_pack}
+ resource_value = {
+ 'uuid' : port["port_name"]
+ , 'type' : port["port_qual"] if "port_qual" in port else None,
+ 'location': {"circuit_pack":location.circuit_pack}
}
ports_result.append((resource_key, resource_value))
+
for interface in interfaces:
existed=False
circuit_name=interface['circuit_pack_name']
interface_list=interface['interface_list']
-
+ if 'circuits' in dic and len (dic['circuits']):
+ target= next((item for item in dic['circuits'] if item.get('circuit_pack_name') == circuit_name), None)
+ if target :
+ target ['interface_list'] = interface_list
location_interface=f'{interface_list}/{circuit_name}'
port = interface["port"]
type = interface['type']
@@ -237,9 +238,10 @@ def openroadm_values_extractor (data_xml:str,resource_keys:list,dic:dict):
if not existed:
resource_key = '/endpoints/endpoint[{:s}]'.format(port)
- resource_value = {'uuid': f'{port}'
- , 'type':type ,
- 'location':{"interface":location_interface}
+ resource_value = {
+ 'uuid' : f'{port}'
+ , 'type' : type ,
+ 'location': {"interface":location_interface}
}
ports_result.append((resource_key, resource_value))
diff --git a/src/opticalcontroller/OpticalController.py b/src/opticalcontroller/OpticalController.py
index 4c211f9789f802da00ccfc6a3bce3cac35000a98..3e7cc28c5099553d56afa840acc7caf43874cede 100644
--- a/src/opticalcontroller/OpticalController.py
+++ b/src/opticalcontroller/OpticalController.py
@@ -107,13 +107,13 @@ class AddFlexLightpath(Resource):
else:
return "Error", 404
# @optical.route('/DelFlexLightpath////')
-@optical.route('/DelFlexLightpath////')
-@optical.route('/DelFlexLightpath/////')
+@optical.route('/DelFlexLightpath////')
+@optical.route('/DelFlexLightpath/////')
@optical.response(200, 'Success')
@optical.response(404, 'Error, not found')
class DelFLightpath(Resource):
@staticmethod
- def delete( src, dst, bitrate, o_band_id, flow_id=None):
+ def delete( src, dst, bitrate, o_band_id=None, flow_id=None):
flow = None
match1=False
ob_id=None
@@ -122,18 +122,20 @@ class DelFLightpath(Resource):
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"]
+ ob_id = flow["parent_opt_band"] if 'parent_opt_band' in flow else None
flow['is_active']=False
if flow is not None:
bidir = flow["bidir"]
if bidir:
match2 = flow["src"] == dst and flow["dst"] == src and flow["bitrate"] == bitrate
if match1 or match2:
- ob_id = flow["parent_opt_band"]
- rsa.del_flow(flow, flow_id, ob_id)
+ ob_id = flow["parent_opt_band"] if 'parent_opt_band' in flow else None
rsa.db_flows[flow_id]["is_active"] = False
- if flow_id in rsa.optical_bands[ob_id]["served_lightpaths"]:
- rsa.optical_bands[ob_id]["served_lightpaths"].remove(flow_id)
+ if ob_id:
+ rsa.del_flow(flow, flow_id, ob_id)
+ if flow_id in rsa.optical_bands[ob_id]["served_lightpaths"]:
+ rsa.optical_bands[ob_id]["served_lightpaths"].remove(flow_id)
+
#if rsa.optical_bands[ob_id]["reverse_optical_band_id"] != 0:
# rev_ob_id = rsa.optical_bands[ob_id]["reverse_optical_band_id"]
# rsa.optical_bands[rev_ob_id]["served_lightpaths"].remove(flow_id)
@@ -151,12 +153,15 @@ class DelFLightpath(Resource):
# if len( rsa.optical_bands[ob_id]["served_lightpaths"]) != 0:
# return "DELETE_NOT_ALLOWED" ,400
- ob_id = flow["parent_opt_band"]
- rsa.del_flow(flow,flow_id,ob_id)
+ ob_id = flow["parent_opt_band"] if 'parent_opt_band' in flow else None
+ if ob_id:
+ rsa.del_flow(flow,flow_id,ob_id)
if debug:
- print(f"vor ob_id {ob_id} rsa.optical_bands {rsa.optical_bands[ob_id]}")
- print(f"rsa.links_dict {rsa.links_dict}")
+ print(f"rsa.links_dict {rsa.links_dict}")
+ if ob_id:
+ print(f"vor ob_id {ob_id} rsa.optical_bands {rsa.optical_bands[ob_id]}")
+
return "flow {} deleted".format(flow_id), 200
else:
return "flow {} not matching".format(flow_id), 404
@@ -263,7 +268,7 @@ class DelOpticalBandSimple(Resource):
-@optical.route('/DelLightpath////')
+@optical.route('/DelLightpath////')
@optical.response(200, 'Success')
@optical.response(404, 'Error, not found')
class DelLightpath(Resource):
@@ -274,7 +279,7 @@ class DelLightpath(Resource):
match1 = flow["src"] == src and flow["dst"] == dst and flow["bitrate"] == bitrate
match2 = flow["src"] == dst and flow["dst"] == src and flow["bitrate"] == bitrate
if match1 or match2:
- rsa.del_flow(flow)
+ rsa.del_flow(flow,flow_id)
rsa.db_flows[flow_id]["is_active"] = False
if debug:
print(rsa.links_dict)
@@ -355,6 +360,7 @@ class GetTopology(Resource):
global rsa
if (rsa is not None):
+
return "Opticalcontroller is synchronised" ,200
topog_id = TopologyId()
topog_id.topology_uuid.uuid=topology_id
@@ -368,6 +374,9 @@ class GetTopology(Resource):
OPTICAL_ROADM_TYPES = {
DeviceTypeEnum.OPTICAL_ROADM.value, DeviceTypeEnum.EMULATED_OPTICAL_ROADM.value
}
+ OPTICAL_OPENROADM_TYPES = {
+ DeviceTypeEnum.OPEN_ROADM.value, DeviceTypeEnum.EMULATED_OPEN_ROADM.value
+ }
OPTICAL_TRANSPONDER_TYPES = {
DeviceTypeEnum.OPTICAL_TRANSPONDER.value, DeviceTypeEnum.EMULATED_OPTICAL_TRANSPONDER.value
}
@@ -377,6 +386,8 @@ class GetTopology(Resource):
dev_type = "OC-ROADM"
elif device.device_type in OPTICAL_TRANSPONDER_TYPES:
dev_type = "OC-TP"
+ elif device.device_type in OPTICAL_OPENROADM_TYPES:
+ dev_type = "OC-ROADM"
else:
continue
diff --git a/src/opticalcontroller/RSA.py b/src/opticalcontroller/RSA.py
index b111ea93da0b253f1f056d4cbabff213562a7e58..8c445fe7202928e7878f054205fb5841face76ed 100644
--- a/src/opticalcontroller/RSA.py
+++ b/src/opticalcontroller/RSA.py
@@ -126,7 +126,7 @@ class RSA():
path = shortest_path(self.g, self.g.get_vertex(src), self.g.get_vertex(dst))
print("INFO: Path from {} to {} with distance: {}".format(src, dst, self.g.get_vertex(dst).get_distance()))
if debug:
- print(path)
+ print(f"compute_path shortest_path {path}")
links = []
for i in range(0, len(path) - 1):
s = path[i]
@@ -300,14 +300,14 @@ class RSA():
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_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):
@@ -353,9 +353,9 @@ class RSA():
link = self.get_link_by_name(l)
fib = link["optical_details"]
#self.restore_link(fib, slots, band)
- self.restore_link_2(fib, slots, band, link=link)
+ self.restore_link(fib, slots, band)
if debug:
- print(fib[band])
+ print(f"fib[band] {fib[band]}")
if o_b_id is not None:
if debug:
print("restoring OB")
@@ -370,13 +370,13 @@ class RSA():
for l in links:
r_l = reverse_link(l)
if debug:
- print(r_l)
+ print(f"reverse_link {r_l}")
rlink = self.get_link_by_name(r_l)
fib = rlink["optical_details"]
#fib = self.get_link_by_name(r_l)["optical_details"]
if list_in_list(slots, str_list_to_int(fib[band].keys())):
#self.restore_link(fib, slots, band, link=l)
- self.restore_link_2(fib, slots, band, link=rlink)
+ self.restore_link(fib, slots, band)
if debug:
print(fib[band])
'''
diff --git a/src/service/service/ServiceServiceServicerImpl.py b/src/service/service/ServiceServiceServicerImpl.py
index 0e8ae981f4af59df512f741a49513dc90eaf7d24..bfa8f27b75b1779f152b846bc2875578895c00e1 100644
--- a/src/service/service/ServiceServiceServicerImpl.py
+++ b/src/service/service/ServiceServiceServicerImpl.py
@@ -260,7 +260,7 @@ class ServiceServiceServicerImpl(ServiceServiceServicer):
DEFAULT_TOPOLOGY_NAME, context_id_x)
topology_details = context_client.GetTopologyDetails(
TopologyId(**topology_id_x))
-
+
refresh_opticalcontroller(topology_id_x)
# devices = get_devices_in_topology(context_client, TopologyId(**topology_id_x), ContextId(**context_id_x))
devices = topology_details.devices
@@ -270,8 +270,8 @@ class ServiceServiceServicerImpl(ServiceServiceServicer):
device_names : Dict[str, str] = dict()
for device in devices:
device_uuid = device.device_id.device_uuid.uuid
- device_names[device_uuid] = device.name # ID => name
- device_names[device.name] = device.name # name => name (that way, if not present, crash)
+ device_names[device_uuid] = device.name
+ device_names[device.name] = device.name
devs = []
ports = []
@@ -283,6 +283,7 @@ class ServiceServiceServicerImpl(ServiceServiceServicer):
endpoint_device_name = device_names[endpoint_device_uuid]
devs.append(endpoint_device_name)
ports.append(endpoint_id.endpoint_uuid.uuid)
+
src = devs[0]
dst = devs[1]
bidir = None
@@ -449,6 +450,30 @@ class ServiceServiceServicerImpl(ServiceServiceServicer):
params['bidir' ] = bidir
+ tasks_scheduler = TasksScheduler(self.service_handler_factory)
+ tasks_scheduler.compose_from_optical_service(service, params=params, is_delete=True)
+ tasks_scheduler.execute_all()
+ return Empty()
+ elif oc_type ==2 :
+
+ if len(service.service_config.config_rules) > 0:
+ c_rules_dict = json.loads(
+ service.service_config.config_rules[0].custom.resource_value)
+ ob_id=None
+ flow_id=None
+
+ if ("flow_id" in c_rules_dict):
+ flow_id = c_rules_dict["flow_id"]
+ #if ("ob_id" in c_rules_dict):
+ # ob_id = c_rules_dict["ob_id"]
+ params['bitrate']=bitrate
+ params['dst']=dst
+ params['src']=src
+ params['ob_id']=ob_id
+ params['flow_id']=flow_id
+ params['bidir'] = bidir
+
+
tasks_scheduler = TasksScheduler(self.service_handler_factory)
tasks_scheduler.compose_from_optical_service(service, params=params, is_delete=True)
tasks_scheduler.execute_all()
diff --git a/src/service/service/service_handler_api/FilterFields.py b/src/service/service/service_handler_api/FilterFields.py
index 14f975316ce3c83d95467b1a99549d5862dd3d59..61b1cbe04fa3ae5acfe9010ce687729b15c2db0c 100644
--- a/src/service/service/service_handler_api/FilterFields.py
+++ b/src/service/service/service_handler_api/FilterFields.py
@@ -55,6 +55,7 @@ DEVICE_DRIVER_VALUES = {
DeviceDriverEnum.DEVICEDRIVER_MORPHEUS,
DeviceDriverEnum.DEVICEDRIVER_RYU,
DeviceDriverEnum.DEVICEDRIVER_GNMI_NOKIA_SRLINUX,
+ DeviceDriverEnum.DEVICEDRIVER_OPENROADM,
}
# Map allowed filter fields to allowed values per Filter field. If no restriction (free text) None is specified
diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py
index 10a7effc225858ae966e7f4543b1711789ccfb54..224ebabb303d185655a97fbddc189237e3861876 100644
--- a/src/service/service/service_handlers/__init__.py
+++ b/src/service/service/service_handlers/__init__.py
@@ -165,7 +165,10 @@ SERVICE_HANDLERS = [
(OCServiceHandler, [
{
FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY,
- FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_OC,
+ FilterFieldEnum.DEVICE_DRIVER : [
+ DeviceDriverEnum.DEVICEDRIVER_OC,
+ DeviceDriverEnum.DEVICEDRIVER_OPENROADM
+ ],
}
]),
(IP_LinkServiceHandler, [
diff --git a/src/service/service/service_handlers/oc/OCServiceHandler.py b/src/service/service/service_handlers/oc/OCServiceHandler.py
index 01ebaebbe1e5ec31e9e5c8d4b6888dead103ff24..d2a9fd58a3ac2dc679dd2cca4486b1b8925a3812 100644
--- a/src/service/service/service_handlers/oc/OCServiceHandler.py
+++ b/src/service/service/service_handlers/oc/OCServiceHandler.py
@@ -25,8 +25,8 @@ 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 (
- endpoints_to_flows,convert_endpoints_to_flows
- #handle_flows_names, check_media_channel_existance
+ endpoints_to_flows,convert_endpoints_to_flows,
+ convert_or_endpoints_to_flows
)
LOGGER = logging.getLogger(__name__)
@@ -81,8 +81,6 @@ class OCServiceHandler(_ServiceHandler):
self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None
) -> List[Union[bool, Exception]]:
- LOGGER.info(f"endpoints {endpoints} ")
-
chk_type('endpoints', endpoints, list)
if len(endpoints) == 0: return []
is_opticalband =False
@@ -96,30 +94,18 @@ class OCServiceHandler(_ServiceHandler):
settings = self.__settings_handler.get('/settings')
bidir = settings.value.get("bidir")
- LOGGER.debug(f"settings bvalue is: {settings}")
- # settings = self.__settings_handler.get('/settings')
-
- # in case service expanded , the only part to reconfigure is optical band
ob_expansion =settings.value.get('ob-expanded',None)
if ob_expansion :
if not is_opticalband:
- LOGGER.debug(f"ob-expanded bvalue is: {ob_expansion} and is_opticalband {is_opticalband}")
+ LOGGER.info(f"ob-expanded bvalue is: {ob_expansion} and is_opticalband {is_opticalband}")
return results
- LOGGER.info(f"endpoints {endpoints} is_opticalband {is_opticalband} ")
- #flow is the new variable that stores input-output relationship
- #flows = convert_endpoints_to_flows(endpoints)
flows = endpoints_to_flows(endpoints, bidir, is_opticalband)
- LOGGER.info(f"endpoints {flows} is_opticalband {is_opticalband} ")
- #handled_flows=handle_flows_names(flows=flows,task_executor=self.__task_executor)
-
-
#new cycle for setting optical devices
for device_uuid, dev_flows in flows.items():
try:
device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
- LOGGER.info(f"device {device_obj.name} ")
if settings is not None:
self.__task_executor.configure_optical_device(device_obj, settings, dev_flows
, is_opticalband ,connection_uuid)
@@ -136,7 +122,7 @@ class OCServiceHandler(_ServiceHandler):
self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None
) -> List[Union[bool, Exception]]:
is_opticalband =False
-
+ is_openroadm =False
service_uuid = self.__service.service_id.service_uuid.uuid
chk_type('endpoints', endpoints, list)
if len(endpoints) == 0: return []
@@ -146,16 +132,18 @@ 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)
- flows = convert_endpoints_to_flows(endpoints)
- results = []
+ results = []
for endpoint in endpoints:
try:
device_uuid, endpoint_uuid = get_device_endpoint_uuids(endpoint)
-
device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+
+ # temporarily until updating convert to flow , cause it doesn't support openroadm endpoints flows
+ if device_obj.device_type == DeviceTypeEnum.OPEN_ROADM._value_ :
+ if not is_openroadm: is_openroadm=True
device_settings = self.__settings_handler.get_device_settings(device_obj)
endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid)
endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj)
@@ -175,6 +163,12 @@ class OCServiceHandler(_ServiceHandler):
except Exception as e: # pylint: disable=broad-except
LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint)))
results.append(e)
+
+ if is_openroadm:
+ flows = convert_or_endpoints_to_flows(endpoints, bidir)
+ else:
+ flows = endpoints_to_flows(endpoints, bidir, is_opticalband)
+
for device_uuid, dev_flows in flows.items():
try:
channel_indexes= []
@@ -192,24 +186,22 @@ class OCServiceHandler(_ServiceHandler):
dst_endpoint_obj = get_endpoint_matching(device_obj, dst)
dist_enpoint_name=dst_endpoint_obj.name
channel_indexes.append((src_enpoint_name,dist_enpoint_name))
- else:
+ elif(device_obj.device_type == DeviceTypeEnum.OPTICAL_ROADM._value_):
if not is_opticalband:
if 'flow_id' in settings.value:
channel_indexes.append(settings.value["flow_id"])
elif is_opticalband:
if "ob_id" in settings.value:
channel_indexes.append(settings.value["ob_id"])
-
+ elif (device_obj.device_type == DeviceTypeEnum.OPEN_ROADM._value_):
+ channel_indexes.append(settings.value["flow_id"])
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,
bidir=bidir
)
- # if (len(errors)==0):
- # service_id =self.__service.service_id
- # if not is_opticalband :
- # self.__task_executor.delete_setting(service_id,"/settings","value")
+
results.append(True)
except Exception as e: # pylint: disable=broad-except
LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint)))
@@ -256,8 +248,6 @@ class OCServiceHandler(_ServiceHandler):
def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
chk_type('resources', resources, list)
if len(resources) == 0: return []
- service_id = self.__service.service_id
-
results = []
for resource in resources:
try:
diff --git a/src/service/service/service_handlers/oc/OCTools.py b/src/service/service/service_handlers/oc/OCTools.py
index 24bc7b384dab6edd3c44b62a7448914857b86ee9..14cd7cbedd1c0aa4390067f7244baead946b84a9 100644
--- a/src/service/service/service_handlers/oc/OCTools.py
+++ b/src/service/service/service_handlers/oc/OCTools.py
@@ -20,12 +20,11 @@ from service.service.service_handler_api.Tools import get_endpoint_matching
log = logging.getLogger(__name__)
+
+
def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]])->Dict:
- #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
@@ -33,7 +32,6 @@ def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]])
while(i < end):
endpoint = endpoints[i]
device_uuid, endpoint_uuid = endpoint[0:2]
- log.info("current OCTools step {}, {}, {}".format(i, device_uuid, endpoint_uuid))
if device_uuid not in entries.keys():
entries[device_uuid] = []
if i == 0:
@@ -41,7 +39,7 @@ def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]])
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:
+ if next_device_uuid == device_uuid :
bidir = 1
log.info("connection is bidirectional")
entry_tuple = next_endpoint_uuid, "0"
@@ -63,7 +61,6 @@ def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]])
entry_tuple = endpoint_uuid, next_endpoint_uuid
entries[device_uuid].append(entry_tuple)
i = i + 1
- log.info("current OCTools step {}, {}, {}".format(i, next_device_uuid, device_uuid))
else:
log.debug("ERROR in unidirectional connection 4")
return {}
@@ -103,6 +100,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)
@@ -115,101 +114,67 @@ def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[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]]]
+
+def convert_or_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]],bidir:bool)->Dict:
+
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)
+ if bidir:
+ log.info(f"i starts with {i} ")
+ i = i + 1
+ while(i < end-2):
+ #i
+ endpoint = endpoints[i]
+ device_uuid, endpoint_uuid = endpoint[0:2]
+
+ if device_uuid not in entries.keys():
+ entries[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:
- bidir = 1
- log.info("connection is bidirectional")
- entry_tuple = next_endpoint_uuid, "0"
+ entry_tuple = endpoint_uuid, next_endpoint_uuid
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 {}
+
+ return {}
+ #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]
+ 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)
+ i = i + 4
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]
- 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)
- i = i + 3
- else:
- log.debug("ERROR in bidirection connection 4")
- return {}
- i = i + 1
- return entries
+ return {}
+ else:
+ i +=1
+ while(i < end-1):
+ #i
+ endpoint = endpoints[i]
+ device_uuid, endpoint_uuid = endpoint[0:2]
+ if device_uuid not in entries.keys():
+ entries[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)
+ i = i + 2
+ else:
+ return {}
+
+ return entries
@@ -387,7 +352,7 @@ def conn_flows(endpoints : List[Tuple[str, str, Optional[str]]], bidir : int):
i = i + 2
else:
return {}
- #rx tp
+ #rx tp
endpoint = endpoints[i]
device_uuid, endpoint_uuid = endpoint[0:2]
if device_uuid not in entries.keys():
diff --git a/src/service/service/task_scheduler/TaskScheduler.py b/src/service/service/task_scheduler/TaskScheduler.py
index 5c777a811b42b7da63e03ac215e5266cff800bef..eb61948962aa77c0d4e946146a76d5efcbb11ae5 100644
--- a/src/service/service/task_scheduler/TaskScheduler.py
+++ b/src/service/service/task_scheduler/TaskScheduler.py
@@ -34,8 +34,8 @@ from .tasks.Task_ServiceDelete import Task_ServiceDelete
from .tasks.Task_ServiceSetStatus import Task_ServiceSetStatus
from .TaskExecutor import CacheableObjectType, TaskExecutor
from .tasks.Task_OpticalServiceConfigDelete import Task_OpticalServiceConfigDelete
-from service.service.tools.OpticalTools import delete_lightpath
-
+from service.service.tools.OpticalTools import delete_lightpath ,DelFlexLightpath
+from common.Constants import OpticalServiceType
if TYPE_CHECKING:
from service.service.service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory
@@ -347,25 +347,6 @@ class TasksScheduler:
)
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))
@@ -396,20 +377,30 @@ class TasksScheduler:
if str_item_key in explored_items: continue
connections = self._context_client.ListConnections(item.service_id)
has_media_channel,has_optical_band=self.check_service_for_media_channel(connections=connections,item=item.service_id)
-
+ oc_type = 1
if len(service.service_config.config_rules) > 0:
-
-
- reply,code = delete_lightpath(
- params['src']
- ,params ['dst']
+ for constraint in service.service_constraints:
+ if "type" in constraint.custom.constraint_type:
+ oc_type = OpticalServiceType(str(constraint.custom.constraint_value))
+ if oc_type == 2 :
+ reply,code = delete_lightpath(
+ params['src']
+ , params ['dst']
, params['bitrate']
- , params['ob_id']
- ,delete_band=not has_media_channel
, flow_id= params['flow_id']
)
-
+ else :
+ reply,code = DelFlexLightpath(
+ params['src']
+ , params ['dst']
+ , params['bitrate']
+ , params['ob_id']
+ , delete_band=not has_media_channel
+ , flow_id= params['flow_id']
+ )
+
+
if code == 400 and reply_not_allowed in reply :
MSG = 'Deleteion for the service is not Allowed , Served Lightpaths is not empty'
raise Exception(MSG)
@@ -443,22 +434,19 @@ class TasksScheduler:
self._add_connection_to_executor_cache(connection)
pending_items_to_explore.put(connection)
-
-
-
-
+
explored_items.add(str_item_key)
elif isinstance(item, Connection):
-
-
if code == 400 and reply_not_allowed in reply:break
-
str_item_key = grpc_message_to_json_string(item.connection_id)
if str_item_key in explored_items: continue
- connection_key = include_connection(item.connection_id, item.service_id,has_media_channel=has_media_channel,has_optical_band=has_optical_band)
+ connection_key = include_connection( item.connection_id
+ , item.service_id
+ , has_media_channel=has_media_channel
+ , has_optical_band=has_optical_band )
self._add_connection_to_executor_cache(connection)
if include_service_config is not None :
@@ -469,9 +457,7 @@ class TasksScheduler:
if has_optical_band and is_media_channel:
include_service_config(item.connection_id
- , item.service_id
-
- )
+ , item.service_id )
self._executor.get_service(item.service_id)
@@ -479,7 +465,9 @@ class TasksScheduler:
for sub_service_id in item.sub_service_ids:
- _,service_key_done = include_service(sub_service_id,has_media_channel=has_media_channel,has_optical_band=has_optical_band)
+ _,service_key_done = include_service(sub_service_id
+ ,has_media_channel=has_media_channel
+ ,has_optical_band=has_optical_band)
self._executor.get_service(sub_service_id)
self._dag.add(service_key_done, connection_key)
pending_items_to_explore.put(sub_service_id)
diff --git a/src/service/service/task_scheduler/tasks/Task_OpticalConnectionConfigure.py b/src/service/service/task_scheduler/tasks/Task_OpticalConnectionConfigure.py
index 8a991326c453c625f174ff8e7125f21f338a4244..0cfe6cf9930ae14fa5986aa9fadb24bd1510750e 100644
--- a/src/service/service/task_scheduler/tasks/Task_OpticalConnectionConfigure.py
+++ b/src/service/service/task_scheduler/tasks/Task_OpticalConnectionConfigure.py
@@ -15,12 +15,15 @@
from typing import TYPE_CHECKING, Dict, Tuple
from common.DeviceTypes import DeviceTypeEnum
from common.method_wrappers.ServiceExceptions import OperationFailedException
-from common.proto.context_pb2 import ConnectionId, Device
+from common.proto.context_pb2 import ConnectionId, Device , ServiceTypeEnum
+
from common.tools.grpc.Tools import grpc_message_to_json_string
from service.service.service_handler_api.Tools import check_errors_setendpoint
from service.service.task_scheduler.TaskExecutor import TaskExecutor
from service.service.tools.EndpointIdFormatters import endpointids_to_raw
from service.service.tools.ObjectKeys import get_connection_key
+from service.service.service_handlers.oc.OCServiceHandler import OCServiceHandler
+
from ._Task import _Task
import logging
@@ -28,7 +31,8 @@ import logging
if TYPE_CHECKING:
from service.service.service_handler_api._ServiceHandler import _ServiceHandler
-KEY_TEMPLATE = 'optical_Connection ({connection_id:s}):configure'
+KEY_TEMPLATE = 'optical_Connection ({connection_id:s}):configure'
+supported_handlers = (OCServiceHandler)
class Task_OpticalConnectionConfigure(_Task):
def __init__(self, task_executor : TaskExecutor, connection_id : ConnectionId) -> None:
@@ -56,14 +60,20 @@ class Task_OpticalConnectionConfigure(_Task):
for _, (handler, connection_devices) in service_handlers.items():
if service_handler is None : service_handler=handler
else :
- if type(handler) != type(service_handler) :
- raise Exception("Devices are not compatible ")
-
+ logging.info(f"type_servicehandler {handler} and {service_handler}")
+ if type(handler) != type(service_handler) :
+ if service.service_type == ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY:
+ if isinstance(handler, supported_handlers) and isinstance(service_handler, supported_handlers):
+ break
+
+ raise Exception("Devices are not compatible ")
+
connection_uuid = connection.connection_id.connection_uuid.uuid
endpointids_to_set = endpointids_to_raw(connection.path_hops_endpoint_ids)
errors = list()
-
+
connection_uuid = connection.connection_id.connection_uuid.uuid
+
results_setendpoint = service_handler.SetEndpoint(endpointids_to_set, connection_uuid=connection_uuid)
errors.extend(check_errors_setendpoint(endpointids_to_set, results_setendpoint))
@@ -74,7 +84,7 @@ class Task_OpticalConnectionConfigure(_Task):
raise OperationFailedException(MSG.format(str_connection, str_service), extra_details=errors)
self._task_executor.set_connection(connection)
-
+
results_setendOpticalConfigs = service_handler.SetOpticalConfig(
endpointids_to_set, connection_uuid=connection_uuid
)
@@ -85,4 +95,3 @@ class Task_OpticalConnectionConfigure(_Task):
str_connection = grpc_message_to_json_string(connection)
str_service = grpc_message_to_json_string(service)
raise OperationFailedException(MSG.format(str_connection, str_service), extra_details=errors)
-
diff --git a/src/service/service/task_scheduler/tasks/Task_OpticalConnectionDeconfigure.py b/src/service/service/task_scheduler/tasks/Task_OpticalConnectionDeconfigure.py
index 9a43e5e0f7ce0843d84d1de2644e2c8099a0690d..0bebd7bd09476567b0532c68d5de0116b3ca03c1 100644
--- a/src/service/service/task_scheduler/tasks/Task_OpticalConnectionDeconfigure.py
+++ b/src/service/service/task_scheduler/tasks/Task_OpticalConnectionDeconfigure.py
@@ -20,6 +20,7 @@ from service.service.task_scheduler.TaskExecutor import TaskExecutor
from service.service.tools.EndpointIdFormatters import endpointids_to_raw
from service.service.tools.ObjectKeys import get_connection_key
from ._Task import _Task
+from service.service.service_handlers.openroadm.OpenROADMServiceHandler import OpenROADMServiceHandler
KEY_TEMPLATE = 'optical_connection({connection_id:s}):deconfigure'
@@ -48,37 +49,31 @@ class Task_OpticalConnectionDeconfigure(_Task):
service = self._task_executor.get_service(connection.service_id)
errors = []
service_handler_settings = {}
- service_handlers = self._task_executor.get_service_handlers(
- connection, service, **service_handler_settings
- )
- # TODO: improve to select different service handlers when needed
- # By now, assume a single service handler is retrieved for all the
- # device types in the path, i.e., all entries carry the same
- # service handler, so we choose the first one retrieved.
- if len(service_handlers) < 1:
- raise Exception('Unsupported case: {:s}'.format(str(service_handlers)))
- service_handler,_ = list(service_handlers.values())[0]
-
- endpointids_to_delete = endpointids_to_raw(connection.path_hops_endpoint_ids)
- connection_uuid = connection.connection_id.connection_uuid.uuid
- if self._has_media_channel:
- is_media_channel = service_handler.check_media_channel(connection_uuid)
- if is_media_channel:
+
+ service_handlers = self._task_executor.get_service_handlers(connection, service, **service_handler_settings)
+ for _, (service_handler, connection_devices) in service_handlers.items():
+
+ endpointids_to_delete = endpointids_to_raw(connection.path_hops_endpoint_ids)
+ connection_uuid = connection.connection_id.connection_uuid.uuid
+ if self._has_media_channel:
+ if isinstance(service_handler,OpenROADMServiceHandler):is_media_channel=True
+ else : is_media_channel = service_handler.check_media_channel(connection_uuid)
+ if is_media_channel:
+ results_deleteendpoint = service_handler.DeleteEndpoint(endpointids_to_delete, connection_uuid=connection_uuid)
+ errors = check_errors_deleteendpoint(endpointids_to_delete, results_deleteendpoint)
+ if len(errors) > 0:
+ MSG = 'DeleteEndpoint for OpticalConnection({:s}) from Service({:s})'
+ str_connection = grpc_message_to_json_string(connection)
+ str_service = grpc_message_to_json_string(service)
+ raise OperationFailedException(MSG.format(str_connection, str_service), extra_details=errors)
+ if is_media_channel:
+ self._task_executor.delete_connection(self._connection_id)
+ else:
results_deleteendpoint = service_handler.DeleteEndpoint(endpointids_to_delete, connection_uuid=connection_uuid)
errors = check_errors_deleteendpoint(endpointids_to_delete, results_deleteendpoint)
- if len(errors) > 0:
- MSG = 'DeleteEndpoint for OpticalConnection({:s}) from Service({:s})'
- str_connection = grpc_message_to_json_string(connection)
- str_service = grpc_message_to_json_string(service)
- raise OperationFailedException(MSG.format(str_connection, str_service), extra_details=errors)
- if is_media_channel:
+ if len(errors) > 0:
+ MSG = 'DeleteEndpoint for OpticalConnection({:s}) from Service({:s})'
+ str_connection = grpc_message_to_json_string(connection)
+ str_service = grpc_message_to_json_string(service)
+ raise OperationFailedException(MSG.format(str_connection, str_service), extra_details=errors)
self._task_executor.delete_connection(self._connection_id)
- else:
- results_deleteendpoint = service_handler.DeleteEndpoint(endpointids_to_delete, connection_uuid=connection_uuid)
- errors = check_errors_deleteendpoint(endpointids_to_delete, results_deleteendpoint)
- if len(errors) > 0:
- MSG = 'DeleteEndpoint for OpticalConnection({:s}) from Service({:s})'
- str_connection = grpc_message_to_json_string(connection)
- str_service = grpc_message_to_json_string(service)
- raise OperationFailedException(MSG.format(str_connection, str_service), extra_details=errors)
- self._task_executor.delete_connection(self._connection_id)
diff --git a/src/service/service/tools/OpticalTools.py b/src/service/service/tools/OpticalTools.py
index 8f03f0338e9b998557166d6f88c279cee3978dff..8ec7dcee8bb8fd0b4fa17d8a2c59fc13439e0b50 100644
--- a/src/service/service/tools/OpticalTools.py
+++ b/src/service/service/tools/OpticalTools.py
@@ -177,13 +177,15 @@ def get_optical_band(idx) -> str:
return optical_band_uni_txt
-def delete_lightpath( src, dst, bitrate, ob_id, delete_band, flow_id=None) -> str:
+def DelFlexLightpath( src, dst, bitrate, ob_id, delete_band, flow_id=None) -> str:
reply = "200"
delete_band = 1 if delete_band else 0
base_url = get_optical_controller_base_url()
if not TESTING:
if flow_id is not None:
- urlx = "{:s}/DelFlexLightpath/{}/{}/{}/{}/{}".format(base_url, src, dst, bitrate, ob_id, flow_id)
+ if ob_id is not None :
+ urlx = "{:s}/DelFlexLightpath/{}/{}/{}/{}/{}".format(base_url, src, dst, bitrate, flow_id,ob_id)
+
else :
#urlx = "http://{}:{}/OpticalTFS/DelOpticalBand/{}/{}/{}".format(OPTICAL_IP, OPTICAL_PORT, src, dst, ob_id)
urlx = "{:s}/DelOpticalBandSimple/{}".format(base_url, ob_id)
@@ -194,15 +196,16 @@ def delete_lightpath( src, dst, bitrate, ob_id, delete_band, flow_id=None) -> st
code = r.status_code
return (reply, code)
-def DelFlexLightpath (flow_id, src, dst, bitrate, o_band_id):
+def delete_lightpath ( src, dst, bitrate, flow_id):
reply = "200"
base_url = get_optical_controller_base_url()
if not TESTING:
- urlx = "{:s}/DelFlexLightpath/{}/{}/{}/{}/{}".format(base_url, flow_id, src, dst, bitrate, o_band_id)
+ urlx = "{:s}/DelLightpath/{}/{}/{}/{}".format(base_url, src, dst, bitrate, flow_id)
headers = {"Content-Type": "application/json"}
r = requests.delete(urlx, headers=headers)
- reply = r.text
- return reply
+ reply = r.text
+ code = r.status_code
+ return (reply, code)
def get_lightpaths() -> str:
base_url = get_optical_controller_base_url()
diff --git a/src/tests/ofc24/r_t.sh b/src/tests/ofc24/r_t.sh
index ec0d57aea38d9b07c5d369186aa332fac4b5aff5..11c1fd9dfadf029d0f7fd079fd1973ba7aea6786 100755
--- a/src/tests/ofc24/r_t.sh
+++ b/src/tests/ofc24/r_t.sh
@@ -27,7 +27,7 @@ docker rm na2
docker network create --subnet=192.168.100.0/24 my-custom-network
-screen -dmS t1 -T xterm sh -c "docker run --name t1 --net my-custom-network -p 10.0.2.4:2023:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/oc23bgp.img:latest bash -c 'cp /files/transponders_x4.xml demoECOC21.xml ; ./startNetconfAgent.sh'"
-screen -dmS t3 -T xterm sh -c "docker run --name na3 --net my-custom-network -p 10.0.2.4:2025:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/flexscale-node.img:latest bash -c 'cp /files/platform_r1.xml init_openconfig-platform.xml ; ./startNetconfAgent.sh'"
-screen -dmS t2 -T xterm sh -c "docker run --name t2 --net my-custom-network -p 10.0.2.4:2024:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/oc23bgp.img:latest bash -c 'cp /files/transponders_x4_2.xml demoECOC21.xml ; ./startNetconfAgent.sh'"
-screen -dmS t4 -T xterm sh -c "docker run --name na2 --net my-custom-network -p 10.0.2.4:2026:2022 -v /home/tfs/tfs-ctrl/src/tests/ofc24/tempOC/files:/files -it asgamb1/flexscale-node.img:latest bash -c 'cp /files/platform_r2.xml init_openconfig-platform.xml ; ./startNetconfAgent.sh'"
+screen -dmS t1 -T xterm sh -c "docker run --name t1 --net my-custom-network -p 2023:2022 -v /home/tfs/tfs-hackfest7/src/tests/ofc24/tempOC/files:/files -it asgamb1/oc23bgp.img:latest bash -c 'cp /files/transponders_x4.xml demoECOC21.xml ; ./startNetconfAgent.sh'"
+screen -dmS t3 -T xterm sh -c "docker run --name na3 --net my-custom-network -p 2025:2022 -v /home/tfs/tfs-hackfest7/src/tests/ofc24/tempOC/files:/files -it asgamb1/flexscale-node.img:latest bash -c 'cp /files/platform_r1.xml init_openconfig-platform.xml ; ./startNetconfAgent.sh'"
+screen -dmS t2 -T xterm sh -c "docker run --name t2 --net my-custom-network -p 2024:2022 -v /home/tfs/tfs-hackfest7/src/tests/ofc24/tempOC/files:/files -it asgamb1/oc23bgp.img:latest bash -c 'cp /files/transponders_x4_2.xml demoECOC21.xml ; ./startNetconfAgent.sh'"
+screen -dmS t4 -T xterm sh -c "docker run --name na2 --net my-custom-network -p 2026:2022 -v /home/tfs/tfs-hackfest7/src/tests/ofc24/tempOC/files:/files -it asgamb1/flexscale-node.img:latest bash -c 'cp /files/platform_r2.xml init_openconfig-platform.xml ; ./startNetconfAgent.sh'"
diff --git a/src/webui/service/__init__.py b/src/webui/service/__init__.py
index bfdfbb9fbafa6fd9e48c7714e8e34407505a21e9..45dfe2c94382378dee05ff6f8355200411313596 100644
--- a/src/webui/service/__init__.py
+++ b/src/webui/service/__init__.py
@@ -74,12 +74,12 @@ def create_app(use_config=None, web_app_root=None):
app = Flask(__name__)
if use_config:
app.config.from_mapping(**use_config)
-
+
app.config.update(HEALTHZ={
'live': liveness,
'ready': readiness
})
-
+
app.register_blueprint(healthz, url_prefix='/healthz')
from webui.service.js.routes import js # pylint: disable=import-outside-toplevel
diff --git a/src/webui/service/optical_link/routes.py b/src/webui/service/optical_link/routes.py
index 242573e2dcf839fe9bba56aa7bea37c1abab30db..2dfd9396407a7b5e44fed7e7b1638b32276585f0 100644
--- a/src/webui/service/optical_link/routes.py
+++ b/src/webui/service/optical_link/routes.py
@@ -174,7 +174,7 @@ def get_optical_bands():
try:
r = requests.get(urlx, headers=headers)
reply = r.json()
- if (reply):
+ if (reply and r.status_code == 200):
optical_bands=reply
ob_keys = optical_bands.keys()
for ob_key in ob_keys :
diff --git a/src/webui/service/opticalconfig/routes.py b/src/webui/service/opticalconfig/routes.py
index 042cb63dd701306db835e96910215bfc78a2016d..4473789a6d8bb33986ef218e69decd02d3ef265a 100644
--- a/src/webui/service/opticalconfig/routes.py
+++ b/src/webui/service/opticalconfig/routes.py
@@ -62,7 +62,8 @@ def home() :
value["channels_number"] = len(value['channels'])
else:
if 'interfaces' in value:
- value["channels_number"] = len(value['interfaces'])
+
+ value["channels_number"] = len([i for i in value["interfaces"] if i["type"] != 'Null'])
# value['operationalMode']=value['operational-mode']
# value['targetOutputPower']=value['target-output-power']
value['opticalconfig_id']=configs.opticalconfig_id
@@ -87,6 +88,7 @@ def show_details(config_uuid):
device_name = ""
config = json.loads(opticalConfig.config)
+ logging.info(f"config {config}")
if "device_name" in config:
device_name = config["device_name"]
@@ -123,16 +125,17 @@ def show_details(config_uuid):
if config_type == DeviceTypeEnum.OPEN_ROADM._value_:
if 'interfaces' in config :
for interface in config["interfaces"]:
- new_config={}
- new_config["name"]=interface["name"] if "name" in interface else ''
- new_config["administrative_state"]=interface[ "administrative_state"]
- new_config["circuit_pack_name"]=interface["circuit_pack_name"]
- new_config["port"]=interface["port"]
- new_config["interface_list"]=interface["interface_list"]
- new_config["frequency"]=interface["frequency"]
- new_config["width"]=interface[ "width"]
- new_config["type"]=interface["type"]
- device_details.append(new_config)
+ if interface["type"] != "Null":
+ new_config={}
+ new_config["name"]=interface["name"] if "name" in interface else ''
+ new_config["administrative_state"]=interface[ "administrative_state"]
+ new_config["circuit_pack_name"]=interface["circuit_pack_name"]
+ new_config["port"]=interface["port"]
+ new_config["interface_list"]=interface["interface_list"]
+ new_config["frequency"]=interface["frequency"]
+ new_config["width"]=interface[ "width"]
+ new_config["type"]=interface["type"]
+ device_details.append(new_config)
LOGGER.info("device details %s",device_details)
return render_template('opticalconfig/details.html', device=device_details,config_id=config_uuid,device_name=device_name,type=config_type)
@@ -329,12 +332,19 @@ def update(config_uuid, channel_name):
@opticalconfig.route('refresh_all',methods=['POST','GET'])
def refresh_all ():
- context_client.connect()
- opticalConfig_list:OpticalConfigList = context_client.GetOpticalConfig(Empty())
- context_client.close()
- device_client.connect()
- device_client.GetDeviceConfiguration(opticalConfig_list)
- device_client.close()
+ try:
+ context_client.connect()
+ opticalConfig_list = context_client.GetOpticalConfig(Empty())
+ device_client.connect()
+ device_client.GetDeviceConfiguration(opticalConfig_list)
+ except Exception as err :
+ flash(f'Connection is failed: `{str(err)}`', 'danger')
+ return redirect(url_for("opticalconfig.home"))
+
+ finally:
+ context_client.close()
+ device_client.close()
+
return home()
diff --git a/src/webui/service/static/topology_icons/openroadm.png b/src/webui/service/static/topology_icons/openroadm.png
new file mode 100644
index 0000000000000000000000000000000000000000..2015c839391120f8830d4ca765b1852d9f7bff83
Binary files /dev/null and b/src/webui/service/static/topology_icons/openroadm.png differ
diff --git a/src/webui/service/templates/base_optical/home.html b/src/webui/service/templates/base_optical/home.html
index bfa9f674c598e7b254e55cb4e9e47acd96558cc5..93eb027fd1ef1c11f405de045d2e8000273eca1a 100644
--- a/src/webui/service/templates/base_optical/home.html
+++ b/src/webui/service/templates/base_optical/home.html
@@ -20,6 +20,8 @@
Optical View
+
+
Optical Configuration :