fix ila names

auto design were changed long ago and these functions did not
apply the changes. Besides there was a confusion between request_element
class where loose is a string, and PathRequest from topology.requests
where loose_list is a list of strings.
This patch corrects the naming and also the tests,
because it used the wrong class to gererate xls services

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
Change-Id: I564b77576459d6cb47767398a2db8138ba6ad1e4
This commit is contained in:
EstherLerouzic
2023-06-02 16:50:50 +02:00
parent defe3bee5c
commit dc68d38293
3 changed files with 113 additions and 117 deletions

View File

@@ -575,24 +575,41 @@ def corresp_names(input_filename, network):
corresp_ila[my_e.from_city] = [name] corresp_ila[my_e.from_city] = [name]
# complete with potential autodesign names: amplifiers # complete with potential autodesign names: amplifiers
for my_l in links: for my_l in links:
name = f'Edfa0_fiber ({my_l.to_city} \u2192 {my_l.from_city})-{my_l.west_cable}' # create names whatever the type and filter them out
if name in ila: names = [f'Edfa_preamp_roadm {my_l.from_city}_from_fiber ({my_l.to_city} \u2192 {my_l.from_city})-{my_l.west_cable}',
if my_l.from_city in corresp_ila.keys(): f'Edfa_booster_roadm {my_l.from_city}_to_fiber ({my_l.from_city} \u2192 {my_l.to_city})-{my_l.east_cable}']
# "east edfa in Stbrieuc to Rennes_STA" is equivalent name as for name in names:
# "Edfa0_fiber (Lannion_CAS → Stbrieuc)-F056" if name in ila:
# "west edfa in Stbrieuc to Rennes_STA" is equivalent name as if my_l.from_city in corresp_ila.keys():
# "Edfa0_fiber (Rennes_STA → Stbrieuc)-F057" # "east edfa in Stbrieuc to Rennes_STA" is equivalent name as
# does not filter names: all types (except boosters) are created. # "Edfa_booster_roadm Stbrieuc_to_fiber (Lannion_CAS → Stbrieuc)-F056"
# in case fibers are splitted the name here is a prefix # "west edfa in Stbrieuc to Rennes_STA" is equivalent name as
corresp_ila[my_l.from_city].append(name) # "Edfa_preamp_roadm Stbrieuc_to_fiber (Rennes_STA → Stbrieuc)-F057"
else: # in case fibers are splitted the name here is a prefix
corresp_ila[my_l.from_city] = [name] corresp_ila[my_l.from_city].append(name)
name = f'Edfa0_fiber ({my_l.from_city} \u2192 {my_l.to_city})-{my_l.east_cable}' else:
if name in ila: corresp_ila[my_l.from_city] = [name]
if my_l.to_city in corresp_ila.keys(): names = [f'Edfa_preamp_roadm {my_l.to_city}_from_fiber ({my_l.from_city} \u2192 {my_l.to_city})-{my_l.east_cable}',
corresp_ila[my_l.to_city].append(name) f'Edfa_booster_roadm {my_l.to_city}_to_fiber ({my_l.to_city} \u2192 {my_l.from_city})-{my_l.west_cable}']
else: for name in names:
corresp_ila[my_l.to_city] = [name] if name in ila:
if my_l.to_city in corresp_ila.keys():
corresp_ila[my_l.to_city].append(name)
else:
corresp_ila[my_l.to_city] = [name]
for node in nodes:
names = [f'east edfa in {node.city}', f'west edfa in {node.city}']
for name in names:
if name in ila:
if node.city in corresp_ila.keys():
# "east edfa in Stbrieuc to Rennes_STA" (created with Eqpt) is equivalent name as
# "east edfa in Stbrieuc" or "west edfa in Stbrieuc" (created with Links sheet)
# depending on link node order
corresp_ila[node.city].append(name)
else:
corresp_ila[node.city] = [name]
# merge fused with ila: # merge fused with ila:
for key, val in corresp_fused.items(): for key, val in corresp_fused.items():
if key in corresp_ila.keys(): if key in corresp_ila.keys():
@@ -781,16 +798,19 @@ def corresp_next_node(network, corresp_ila, corresp_roadm):
user ILA name Stbrieuc covers the two direction. convert.py creates 2 different ILA user ILA name Stbrieuc covers the two direction. convert.py creates 2 different ILA
with possible names (depending on the direction and if the eqpt was defined in eqpt with possible names (depending on the direction and if the eqpt was defined in eqpt
sheet) sheet)
for an ILA and if it is defined in eqpt:
- east edfa in Stbrieuc to Rennes_STA - east edfa in Stbrieuc to Rennes_STA
- west edfa in Stbrieuc to Rennes_STA - west edfa in Stbrieuc to Rennes_STA
- Edfa0_fiber (Lannion_CAS → Stbrieuc)-F056 for an ILA and if it is not defined in eqpt:
- Edfa0_fiber (Rennes_STA → Stbrieuc)-F057 - east edfa in Stbrieuc
- west edfa in Stbrieuc
for a roadm
"Edfa_preamp_roadm node1_from_fiber (siteE → node1)-CABLES#19"
"Edfa_booster_roadm node1_to_fiber (node1 → siteE)-CABLES#19"
next_nodes finds the user defined name of next node to be able to map the path constraints next_nodes finds the user defined name of next node to be able to map the path constraints
- east edfa in Stbrieuc to Rennes_STA next node = Rennes_STA - east edfa in Stbrieuc to Rennes_STA next node = Rennes_STA
- west edfa in Stbrieuc to Rennes_STA next node Lannion_CAS - west edfa in Stbrieuc to Rennes_STA next node Lannion_CAS
Edfa0_fiber (Lannion_CAS → Stbrieuc)-F056 and Edfa0_fiber (Rennes_STA → Stbrieuc)-F057
do not exist
the function supports fiber splitting, fused nodes and shall only be called if the function supports fiber splitting, fused nodes and shall only be called if
excel format is used for both network and service excel format is used for both network and service
""" """
@@ -801,8 +821,8 @@ def corresp_next_node(network, corresp_ila, corresp_roadm):
for ila_elem in ila_list: for ila_elem in ila_list:
# find the node with ila_elem string _in_ the node uid. 'in' is used instead of # find the node with ila_elem string _in_ the node uid. 'in' is used instead of
# '==' to find composed nodes due to fiber splitting in autodesign. # '==' to find composed nodes due to fiber splitting in autodesign.
# eg if elem_ila is 'Edfa0_fiber (Lannion_CAS → Stbrieuc)-F056', # eg if elem_ila is 'east edfa in Stbrieuc to Rennes_STA',
# node uid 'Edfa0_fiber (Lannion_CAS → Stbrieuc)-F056_(1/2)' is possible # node uid 'east edfa in Stbrieuc to Rennes_STA-_(1/2)' is possible
correct_ila_name = next(n.uid for n in network.nodes() if ila_elem in n.uid) correct_ila_name = next(n.uid for n in network.nodes() if ila_elem in n.uid)
temp.remove(ila_elem) temp.remove(ila_elem)
temp.append(correct_ila_name) temp.append(correct_ila_name)

View File

@@ -280,10 +280,8 @@ def correct_xls_route_list(network_filename, network, pathreqlist):
# and last elem in the constraints respectively. Other positions must lead to an error # and last elem in the constraints respectively. Other positions must lead to an error
# caught later on # caught later on
if pathreq.nodes_list and pathreq.source == pathreq.nodes_list[0]: if pathreq.nodes_list and pathreq.source == pathreq.nodes_list[0]:
pathreq.loose_list.pop(0)
pathreq.nodes_list.pop(0) pathreq.nodes_list.pop(0)
if pathreq.nodes_list and pathreq.destination == pathreq.nodes_list[-1]: if pathreq.nodes_list and pathreq.destination == pathreq.nodes_list[-1]:
pathreq.loose_list.pop(-1)
pathreq.nodes_list.pop(-1) pathreq.nodes_list.pop(-1)
# Then process user defined constraints with respect to automatic namings # Then process user defined constraints with respect to automatic namings
temp = deepcopy(pathreq) temp = deepcopy(pathreq)
@@ -309,54 +307,50 @@ def correct_xls_route_list(network_filename, network, pathreqlist):
nodes_suggestion = corresp_ila[n_id] nodes_suggestion = corresp_ila[n_id]
else: else:
nodes_suggestion = [] nodes_suggestion = []
if nodes_suggestion: try:
try: if len(nodes_suggestion) > 1:
if len(nodes_suggestion) > 1: # if there is more than one suggestion, we need to choose the direction
# if there is more than one suggestion, we need to choose the direction # we rely on the next node provided by the user for this purpose
# we rely on the next node provided by the user for this purpose new_n = next(n for n in nodes_suggestion
new_n = next(n for n in nodes_suggestion if n in next_node.keys() and next_node[n]
if n in next_node.keys() and next_node[n] in temp.nodes_list[i:] + [pathreq.destination] and
in temp.nodes_list[i:] + [pathreq.destination] and next_node[n] not in temp.nodes_list[:i])
next_node[n] not in temp.nodes_list[:i]) elif len(nodes_suggestion) == 1:
else: new_n = nodes_suggestion[0]
new_n = nodes_suggestion[0]
if new_n != n_id:
# warns the user when the correct name is used only in verbose mode,
# eg 'a' is a roadm and correct name is 'roadm a' or when there was
# too much ambiguity, 'b' is an ila, its name can be:
# Edfa0_fiber (a → b)-xx if next node is c or
# Edfa0_fiber (c → b)-xx if next node is a
msg = f'Request {pathreq.request_id}: Invalid route node specified:' \
+ f'\n\t\'{n_id}\', replaced with \'{new_n}\''
logger.warning(msg)
pathreq.nodes_list[pathreq.nodes_list.index(n_id)] = new_n
except StopIteration:
# shall not come in this case, unless requested direction does not exist
msg = f'Request {pathreq.request_id}: Invalid route specified {n_id}: could' \
+ ' not decide on direction, skipped!.\nPlease add a valid' \
+ ' direction in constraints (next neighbour node)'
logger.warning(msg)
pathreq.loose_list.pop(pathreq.nodes_list.index(n_id))
pathreq.nodes_list.remove(n_id)
else:
if temp.loose_list[i] == 'LOOSE':
# if no matching can be found in the network just ignore this constraint
# if it is a loose constraint
# warns the user that this node is not part of the topology
msg = f'Request {pathreq.request_id}: Invalid node specified:\n\t\'{n_id}\'' \
+ ', could not use it as constraint, skipped!'
logger.warning(msg)
pathreq.loose_list.pop(pathreq.nodes_list.index(n_id))
pathreq.nodes_list.remove(n_id)
else: else:
msg = f'Request {pathreq.request_id}: Could not find node:\n\t\'{n_id}\' in network' \ if temp.loose == 'LOOSE':
# if no matching can be found in the network just ignore this constraint
# if it is a loose constraint
# warns the user that this node is not part of the topology
msg = f'{pathreq.request_id}: Invalid node specified:\n\t\'{n_id}\', ' \
+ 'could not use it as constraint, skipped!'
logger.warning(msg)
pathreq.nodes_list.remove(n_id)
continue
msg = f'{pathreq.request_id}: Could not find node:\n\t\'{n_id}\' in network' \
+ ' topology. Strict constraint can not be applied.' + ' topology. Strict constraint can not be applied.'
logger.critical(msg)
raise ServiceError(msg) raise ServiceError(msg)
if new_n != n_id:
# warns the user when the correct name is used only in verbose mode,
# eg 'a' is a roadm and correct name is 'roadm a' or when there was
# too much ambiguity, 'b' is an ila, its name can be:
# "east edfa in b to c", or "west edfa in b to a" if next node is c or
# "west edfa in b to c", or "east edfa in b to a" if next node is a
msg = f'Request {pathreq.request_id}: Invalid route node specified:' \
+ f'\n\t\'{n_id}\', replaced with \'{new_n}\''
pathreq.nodes_list[pathreq.nodes_list.index(n_id)] = new_n
except StopIteration:
# shall not come in this case, unless requested direction does not exist
msg = f'Request {pathreq.request_id}: Invalid route specified {n_id}: could' \
+ ' not decide on direction, skipped!.\nPlease add a valid' \
+ ' direction in constraints (next neighbour node)'
pathreq.nodes_list.remove(n_id)
else: else:
if temp.loose_list[i] == 'LOOSE': if temp.loose == 'LOOSE':
logger.warning(f'Request {pathreq.request_id}: Invalid route node specified:\n\t\'{n_id}\'' logger.warning('Invalid route node specified:\n\t\'%s\''
+ ' type is not supported as constraint with xls network input, skipped!') + ' type is not supported as constraint with xls network input,'
pathreq.loose_list.pop(pathreq.nodes_list.index(n_id)) + ' skipped!', n_id)
pathreq.nodes_list.remove(n_id) pathreq.nodes_list.remove(n_id)
else: else:
msg = f'Invalid route node specified \n\t\'{n_id}\'' \ msg = f'Invalid route node specified \n\t\'{n_id}\'' \

View File

@@ -32,7 +32,7 @@ from gnpy.topology.spectrum_assignment import build_oms_list, pth_assign_spectru
from gnpy.tools.convert import convert_file from gnpy.tools.convert import convert_file
from gnpy.tools.json_io import (load_json, load_network, save_network, load_equipment, requests_from_json, from gnpy.tools.json_io import (load_json, load_network, save_network, load_equipment, requests_from_json,
disjunctions_from_json, network_to_json, network_from_json) disjunctions_from_json, network_to_json, network_from_json)
from gnpy.tools.service_sheet import read_service_sheet, correct_xls_route_list from gnpy.tools.service_sheet import read_service_sheet, correct_xls_route_list, Request_element, Request
TEST_DIR = Path(__file__).parent TEST_DIR = Path(__file__).parent
DATA_DIR = TEST_DIR / 'data' DATA_DIR = TEST_DIR / 'data'
@@ -284,44 +284,43 @@ def test_json_response_generation(xls_input, expected_response_file):
@pytest.mark.parametrize('source, destination, route_list, hoptype, expected_correction', [ @pytest.mark.parametrize('source, destination, route_list, hoptype, expected_correction', [
('trx Brest_KLA', 'trx Vannes_KBE', ('Brest_KLA', 'Vannes_KBE',
'roadm Brest_KLA | roadm Lannion_CAS | roadm Lorient_KMA | roadm Vannes_KBE', 'roadm Brest_KLA | roadm Lannion_CAS | roadm Lorient_KMA | roadm Vannes_KBE',
'STRICT', 'no',
['roadm Brest_KLA', 'roadm Lannion_CAS', 'roadm Lorient_KMA', 'roadm Vannes_KBE']), ['roadm Brest_KLA', 'roadm Lannion_CAS', 'roadm Lorient_KMA', 'roadm Vannes_KBE']),
('trx Brest_KLA', 'trx Vannes_KBE', ('Brest_KLA', 'Vannes_KBE',
'trx Brest_KLA | roadm Lannion_CAS | roadm Lorient_KMA | roadm Vannes_KBE', 'trx Brest_KLA | roadm Lannion_CAS | roadm Lorient_KMA | roadm Vannes_KBE',
'STRICT', 'No',
['roadm Lannion_CAS', 'roadm Lorient_KMA', 'roadm Vannes_KBE']), ['roadm Lannion_CAS', 'roadm Lorient_KMA', 'roadm Vannes_KBE']),
('trx Lannion_CAS', 'trx Rennes_STA', 'trx Rennes_STA', 'LOOSE', []), ('Lannion_CAS', 'Rennes_STA', 'trx Rennes_STA', 'yes', []),
('trx Lannion_CAS', 'trx Lorient_KMA', 'toto', 'LOOSE', []), ('Lannion_CAS', 'Lorient_KMA', 'toto', 'yes', []),
('trx Lannion_CAS', 'trx Lorient_KMA', 'toto', 'STRICT', 'Fail'), ('Lannion_CAS', 'Lorient_KMA', 'toto', 'no', 'Fail'),
('trx Lannion_CAS', 'trx Lorient_KMA', 'Corlay | Loudeac | Lorient_KMA', 'LOOSE', ('Lannion_CAS', 'Lorient_KMA', 'Corlay | Loudeac | Lorient_KMA', 'yes',
['west fused spans in Corlay', 'west fused spans in Loudeac', 'roadm Lorient_KMA']), ['west fused spans in Corlay', 'west fused spans in Loudeac', 'roadm Lorient_KMA']),
('trx Lannion_CAS', 'trx Lorient_KMA', 'Ploermel | Vannes_KBE', 'LOOSE', ('Lannion_CAS', 'Lorient_KMA', 'Ploermel | Vannes_KBE', 'yes',
['east edfa in Ploermel to Vannes_KBE', 'roadm Vannes_KBE']), ['east edfa in Ploermel to Vannes_KBE', 'roadm Vannes_KBE']),
('trx Rennes_STA', 'trx Brest_KLA', 'Vannes_KBE | Quimper | Brest_KLA', 'LOOSE', ('Rennes_STA', 'Brest_KLA', 'Vannes_KBE | Quimper | Brest_KLA', 'yes',
['roadm Vannes_KBE', 'west edfa in Quimper to Lorient_KMA', 'roadm Brest_KLA']), ['roadm Vannes_KBE', 'west edfa in Quimper to Lorient_KMA', 'roadm Brest_KLA']),
('trx Brest_KLA', 'trx Rennes_STA', 'Brest_KLA | Quimper | Lorient_KMA', 'LOOSE', ('Brest_KLA', 'Rennes_STA', 'Brest_KLA | Quimper | Lorient_KMA', 'yes',
['roadm Brest_KLA', 'east edfa in Quimper to Lorient_KMA', 'roadm Lorient_KMA']), ['roadm Brest_KLA', 'east edfa in Quimper to Lorient_KMA', 'roadm Lorient_KMA']),
('Brest_KLA', 'trx Rennes_STA', '', 'LOOSE', 'Fail'), ('Brest_KLA', 'trx Rennes_STA', '', 'yes', 'Fail'),
('trx Brest_KLA', 'Rennes_STA', '', 'LOOSE', 'Fail'), ('trx Brest_KLA', 'Rennes_STA', '', 'yes', 'Fail'),
('Brest_KLA', 'Rennes_STA', '', 'LOOSE', 'Fail'), ('trx Brest_KLA', 'trx Rennes_STA', '', 'yes', 'Fail'),
('Brest_KLA', 'trx Rennes_STA', '', 'STRICT', 'Fail'), ('Brest_KLA', 'trx Rennes_STA', '', 'no', 'Fail'),
('trx Brest_KLA', 'trx Rennes_STA', 'trx Rennes_STA', 'STRICT', []), ('Brest_KLA', 'Rennes_STA', 'trx Rennes_STA', 'no', []),
('trx Brest_KLA', 'trx Rennes_STA', None, '', []), ('Brest_KLA', 'Rennes_STA', None, '', []),
('trx Brest_KLA', 'trx Rennes_STA', 'Brest_KLA | Quimper | Ploermel', 'LOOSE', ('Brest_KLA', 'Rennes_STA', 'Brest_KLA | Quimper | Ploermel', 'yes',
['roadm Brest_KLA']), ['roadm Brest_KLA']),
('trx Brest_KLA', 'trx Rennes_STA', 'Brest_KLA | Quimper | Ploermel', 'STRICT', ('Brest_KLA', 'Rennes_STA', 'Brest_KLA | Quimper | Ploermel', 'no',
['roadm Brest_KLA']), ['roadm Brest_KLA']),
('trx Brest_KLA', 'trx Rennes_STA', 'Brest_KLA | trx Quimper', 'LOOSE', ['roadm Brest_KLA']), ('Brest_KLA', 'Rennes_STA', 'Brest_KLA | trx Quimper', 'yes', ['roadm Brest_KLA']),
('trx Brest_KLA', 'trx Rennes_STA', 'Brest_KLA | trx Lannion_CAS', 'LOOSE', ['roadm Brest_KLA']), ('Brest_KLA', 'Rennes_STA', 'Brest_KLA | trx Lannion_CAS', 'yes', ['roadm Brest_KLA']),
('trx Brest_KLA', 'trx Rennes_STA', 'Brest_KLA | trx Lannion_CAS', 'STRICT', 'Fail') ('Brest_KLA', 'Rennes_STA', 'Brest_KLA | trx Lannion_CAS', 'no', 'Fail')
]) ])
def test_excel_ila_constraints(source, destination, route_list, hoptype, expected_correction): def test_excel_ila_constraints(source, destination, route_list, hoptype, expected_correction):
"""add different kind of constraints to test all correct_route cases""" """add different kind of constraints to test all correct_route cases"""
service_xls_input = DATA_DIR / 'testTopology.xls' service_xls_input = DATA_DIR / 'testTopology.xls'
network_json_input = DATA_DIR / 'testTopology_auto_design_expected.json' network_json_input = DATA_DIR / 'testTopology_auto_design_expected.json'
equipment = load_equipment(eqpt_filename)
network = load_network(network_json_input, equipment) network = load_network(network_json_input, equipment)
# increase length of one span to trigger automatic fiber splitting included by autodesign # increase length of one span to trigger automatic fiber splitting included by autodesign
# so that the test also covers this case # so that the test also covers this case
@@ -331,37 +330,20 @@ def test_excel_ila_constraints(source, destination, route_list, hoptype, expecte
p_db = default_si.power_dbm p_db = default_si.power_dbm
p_total_db = p_db + lin2db(automatic_nch(default_si.f_min, default_si.f_max, default_si.spacing)) p_total_db = p_db + lin2db(automatic_nch(default_si.f_min, default_si.f_max, default_si.spacing))
build_network(network, equipment, p_db, p_total_db) build_network(network, equipment, p_db, p_total_db)
# create params for a request based on input # create params for a xls request based on input (note that this not the same type as PathRequest)
nodes_list = route_list.split(' | ') if route_list is not None else []
params = { params = {
'request_id': '0', 'request_id': '0',
'source': source, 'source': source,
'bidir': False,
'destination': destination, 'destination': destination,
'trx_type': '', 'trx_type': 'Voyager',
'trx_mode': '', 'spacing': 50,
'format': '', 'nodes_list': route_list,
'spacing': '', 'is_loose': hoptype,
'nodes_list': nodes_list,
'loose_list': [hoptype for node in nodes_list] if route_list is not None else '',
'f_min': 0,
'f_max': 0,
'baud_rate': 0,
'OSNR': None,
'bit_rate': None,
'cost': None,
'roll_off': 0,
'tx_osnr': 0,
'tx_power': 0,
'penalties': None,
'min_spacing': None,
'nb_channel': 0, 'nb_channel': 0,
'power': 0, 'power': 0,
'path_bandwidth': 0, 'path_bandwidth': 0,
'effective_freq_slot': None,
'equalization_offset_db': 0
} }
request = PathRequest(**params) request = Request_element(Request(**params), equipment, False)
if expected_correction != 'Fail': if expected_correction != 'Fail':
[request] = correct_xls_route_list(service_xls_input, network, [request]) [request] = correct_xls_route_list(service_xls_input, network, [request])