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
names = [f'Edfa_preamp_roadm {my_l.from_city}_from_fiber ({my_l.to_city} \u2192 {my_l.from_city})-{my_l.west_cable}',
f'Edfa_booster_roadm {my_l.from_city}_to_fiber ({my_l.from_city} \u2192 {my_l.to_city})-{my_l.east_cable}']
for name in names:
if name in ila: if name in ila:
if my_l.from_city in corresp_ila.keys(): if my_l.from_city in corresp_ila.keys():
# "east edfa in Stbrieuc to Rennes_STA" is equivalent name as # "east edfa in Stbrieuc to Rennes_STA" is equivalent name as
# "Edfa0_fiber (Lannion_CAS → Stbrieuc)-F056" # "Edfa_booster_roadm Stbrieuc_to_fiber (Lannion_CAS → Stbrieuc)-F056"
# "west edfa in Stbrieuc to Rennes_STA" is equivalent name as # "west edfa in Stbrieuc to Rennes_STA" is equivalent name as
# "Edfa0_fiber (Rennes_STA → Stbrieuc)-F057" # "Edfa_preamp_roadm Stbrieuc_to_fiber (Rennes_STA → Stbrieuc)-F057"
# does not filter names: all types (except boosters) are created.
# in case fibers are splitted the name here is a prefix # in case fibers are splitted the name here is a prefix
corresp_ila[my_l.from_city].append(name) corresp_ila[my_l.from_city].append(name)
else: else:
corresp_ila[my_l.from_city] = [name] corresp_ila[my_l.from_city] = [name]
name = f'Edfa0_fiber ({my_l.from_city} \u2192 {my_l.to_city})-{my_l.east_cable}' names = [f'Edfa_preamp_roadm {my_l.to_city}_from_fiber ({my_l.from_city} \u2192 {my_l.to_city})-{my_l.east_cable}',
f'Edfa_booster_roadm {my_l.to_city}_to_fiber ({my_l.to_city} \u2192 {my_l.from_city})-{my_l.west_cable}']
for name in names:
if name in ila: if name in ila:
if my_l.to_city in corresp_ila.keys(): if my_l.to_city in corresp_ila.keys():
corresp_ila[my_l.to_city].append(name) corresp_ila[my_l.to_city].append(name)
else: else:
corresp_ila[my_l.to_city] = [name] 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,7 +307,6 @@ 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
@@ -318,45 +315,42 @@ def correct_xls_route_list(network_filename, network, pathreqlist):
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])
else: elif len(nodes_suggestion) == 1:
new_n = nodes_suggestion[0] new_n = nodes_suggestion[0]
else:
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.'
logger.critical(msg)
raise ServiceError(msg)
if new_n != n_id: if new_n != n_id:
# warns the user when the correct name is used only in verbose mode, # 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 # 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: # too much ambiguity, 'b' is an ila, its name can be:
# Edfa0_fiber (a → b)-xx if next node is c or # "east edfa in b to c", or "west edfa in b to a" if next node is c or
# Edfa0_fiber (c → b)-xx if next node is a # "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:' \ msg = f'Request {pathreq.request_id}: Invalid route node specified:' \
+ f'\n\t\'{n_id}\', replaced with \'{new_n}\'' + f'\n\t\'{n_id}\', replaced with \'{new_n}\''
logger.warning(msg)
pathreq.nodes_list[pathreq.nodes_list.index(n_id)] = new_n pathreq.nodes_list[pathreq.nodes_list.index(n_id)] = new_n
except StopIteration: except StopIteration:
# shall not come in this case, unless requested direction does not exist # shall not come in this case, unless requested direction does not exist
msg = f'Request {pathreq.request_id}: Invalid route specified {n_id}: could' \ msg = f'Request {pathreq.request_id}: Invalid route specified {n_id}: could' \
+ ' not decide on direction, skipped!.\nPlease add a valid' \ + ' not decide on direction, skipped!.\nPlease add a valid' \
+ ' direction in constraints (next neighbour node)' + ' 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) pathreq.nodes_list.remove(n_id)
else: else:
if temp.loose_list[i] == 'LOOSE': if temp.loose == 'LOOSE':
# if no matching can be found in the network just ignore this constraint logger.warning('Invalid route node specified:\n\t\'%s\''
# if it is a loose constraint + ' type is not supported as constraint with xls network input,'
# warns the user that this node is not part of the topology + ' skipped!', n_id)
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:
msg = f'Request {pathreq.request_id}: Could not find node:\n\t\'{n_id}\' in network' \
+ ' topology. Strict constraint can not be applied.'
raise ServiceError(msg)
else:
if temp.loose_list[i] == 'LOOSE':
logger.warning(f'Request {pathreq.request_id}: Invalid route node specified:\n\t\'{n_id}\''
+ ' type is not supported as constraint with xls network input, skipped!')
pathreq.loose_list.pop(pathreq.nodes_list.index(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])