reorganization: move request processing and request disjunction handling into topology.requests

Change-Id: I14902a2e15cc5fd27530cb294f4f549f6974fd49
This commit is contained in:
Jan Kundrát
2020-05-23 20:43:34 +02:00
parent 94c5281260
commit 1b2b048b47
4 changed files with 129 additions and 135 deletions

View File

@@ -21,18 +21,14 @@ from numpy import mean
from gnpy.core import ansi_escapes
from gnpy.core.utils import automatic_nch
from gnpy.core.network import build_network
from gnpy.core.elements import Roadm
from gnpy.core.utils import lin2db
from gnpy.core.exceptions import (ConfigurationError, EquipmentConfigError, NetworkTopologyError,
ServiceError, DisjunctionError)
from gnpy.topology.request import (ResultElement,
propagate, jsontocsv, compute_path_dsjctn,
requests_aggregation, propagate_and_optimize_mode,
BLOCKING_NOPATH, BLOCKING_NOMODE,
find_reversed_path, correct_json_route_list)
from gnpy.topology.request import (ResultElement, jsontocsv, compute_path_dsjctn, requests_aggregation,
BLOCKING_NOPATH, correct_json_route_list,
deduplicate_disjunctions, compute_path_with_disjunction)
from gnpy.topology.spectrum_assignment import build_oms_list, pth_assign_spectrum
from gnpy.tools.json_io import load_equipment, load_network, load_requests, save_network, requests_from_json, disjunctions_from_json
from copy import deepcopy
from math import ceil
#EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json'
@@ -57,124 +53,6 @@ PARSER.add_argument('-v', '--verbose', action='count', default=0,\
PARSER.add_argument('-o', '--output', type=Path)
def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist):
""" use a list but a dictionnary might be helpful to find path based on request_id
TODO change all these req, dsjct, res lists into dict !
"""
path_res_list = []
reversed_path_res_list = []
propagated_reversed_path_res_list = []
for i, pathreq in enumerate(pathreqlist):
# use the power specified in requests but might be different from the one
# specified for design the power is an optional parameter for requests
# definition if optional, use the one defines in eqt_config.json
print(f'request {pathreq.request_id}')
print(f'Computing path from {pathreq.source} to {pathreq.destination}')
# adding first node to be clearer on the output
print(f'with path constraint: {[pathreq.source] + pathreq.nodes_list}')
# pathlist[i] contains the whole path information for request i
# last element is a transciver and where the result of the propagation is
# recorded.
# Important Note: since transceivers attached to roadms are actually logical
# elements to simulate performance, several demands having the same destination
# may use the same transponder for the performance simulation. This is why
# we use deepcopy: to ensure that each propagation is recorded and not overwritten
total_path = deepcopy(pathlist[i])
print(f'Computed path (roadms):{[e.uid for e in total_path if isinstance(e, Roadm)]}')
# for debug
# print(f'{pathreq.baud_rate} {pathreq.power} {pathreq.spacing} {pathreq.nb_channel}')
if total_path:
if pathreq.baud_rate is not None:
# means that at this point the mode was entered/forced by user and thus a
# baud_rate was defined
total_path = propagate(total_path, pathreq, equipment)
temp_snr01nm = round(mean(total_path[-1].snr+lin2db(pathreq.baud_rate/(12.5e9))), 2)
if temp_snr01nm < pathreq.OSNR:
msg = f'\tWarning! Request {pathreq.request_id} computed path from' +\
f' {pathreq.source} to {pathreq.destination} does not pass with' +\
f' {pathreq.tsp_mode}\n\tcomputedSNR in 0.1nm = {temp_snr01nm} ' +\
f'- required osnr {pathreq.OSNR}'
print(msg)
LOGGER.warning(msg)
pathreq.blocking_reason = 'MODE_NOT_FEASIBLE'
else:
total_path, mode = propagate_and_optimize_mode(total_path, pathreq, equipment)
# if no baudrate satisfies spacing, no mode is returned and the last explored mode
# a warning is shown in the propagate_and_optimize_mode
# propagate_and_optimize_mode function returns the mode with the highest bitrate
# that passes. if no mode passes, then a attribute blocking_reason is added on
# pathreq that contains the reason for blocking: 'NO_PATH', 'NO_FEASIBLE_MODE', ...
try:
if pathreq.blocking_reason in BLOCKING_NOPATH:
total_path = []
elif pathreq.blocking_reason in BLOCKING_NOMODE:
pathreq.baud_rate = mode['baud_rate']
pathreq.tsp_mode = mode['format']
pathreq.format = mode['format']
pathreq.OSNR = mode['OSNR']
pathreq.tx_osnr = mode['tx_osnr']
pathreq.bit_rate = mode['bit_rate']
# other blocking reason should not appear at this point
except AttributeError:
pathreq.baud_rate = mode['baud_rate']
pathreq.tsp_mode = mode['format']
pathreq.format = mode['format']
pathreq.OSNR = mode['OSNR']
pathreq.tx_osnr = mode['tx_osnr']
pathreq.bit_rate = mode['bit_rate']
# reversed path is needed for correct spectrum assignment
reversed_path = find_reversed_path(pathlist[i])
if pathreq.bidir:
# only propagate if bidir is true, but needs the reversed path anyway for
# correct spectrum assignment
rev_p = deepcopy(reversed_path)
print(f'\n\tPropagating Z to A direction {pathreq.destination} to {pathreq.source}')
print(f'\tPath (roadsm) {[r.uid for r in rev_p if isinstance(r,Roadm)]}\n')
propagated_reversed_path = propagate(rev_p, pathreq, equipment)
temp_snr01nm = round(mean(propagated_reversed_path[-1].snr +\
lin2db(pathreq.baud_rate/(12.5e9))), 2)
if temp_snr01nm < pathreq.OSNR:
msg = f'\tWarning! Request {pathreq.request_id} computed path from' +\
f' {pathreq.source} to {pathreq.destination} does not pass with' +\
f' {pathreq.tsp_mode}\n' +\
f'\tcomputedSNR in 0.1nm = {temp_snr01nm} - required osnr {pathreq.OSNR}'
print(msg)
LOGGER.warning(msg)
# TODO selection of mode should also be on reversed direction !!
pathreq.blocking_reason = 'MODE_NOT_FEASIBLE'
else:
propagated_reversed_path = []
else:
msg = 'Total path is empty. No propagation'
print(msg)
LOGGER.info(msg)
reversed_path = []
propagated_reversed_path = []
path_res_list.append(total_path)
reversed_path_res_list.append(reversed_path)
propagated_reversed_path_res_list.append(propagated_reversed_path)
# print to have a nice output
print('')
return path_res_list, reversed_path_res_list, propagated_reversed_path_res_list
def correct_disjn(disjn):
""" clean disjunctions to remove possible repetition
"""
local_disjn = disjn.copy()
for elem in local_disjn:
for dis_elem in local_disjn:
if set(elem.disjunctions_req) == set(dis_elem.disjunctions_req) and\
elem.disjunction_id != dis_elem.disjunction_id:
local_disjn.remove(dis_elem)
return local_disjn
def path_result_json(pathresult):
""" create the response dictionnary
"""
@@ -242,7 +120,7 @@ def main(args):
print(dsjn)
# need to warn or correct in case of wrong disjunction form
# disjunction must not be repeated with same or different ids
dsjn = correct_disjn(dsjn)
dsjn = deduplicate_disjunctions(dsjn)
# Aggregate demands with same exact constraints
print(f'{ansi_escapes.blue}Aggregating similar requests{ansi_escapes.reset}')
@@ -261,8 +139,7 @@ def main(args):
exit(1)
print(f'{ansi_escapes.blue}Propagating on selected path{ansi_escapes.reset}')
propagatedpths, reversed_pths, reversed_propagatedpths = \
compute_path_with_disjunction(network, equipment, rqs, pths)
propagatedpths, reversed_pths, reversed_propagatedpths = compute_path_with_disjunction(network, equipment, rqs, pths)
# Note that deepcopy used in compute_path_with_disjunction returns
# a list of nodes which are not belonging to network (they are copies of the node objects).
# so there can not be propagation on these nodes.

View File

@@ -1110,3 +1110,122 @@ def correct_json_route_list(network, pathreqlist):
raise ServiceError(msg)
return pathreqlist
def deduplicate_disjunctions(disjn):
""" clean disjunctions to remove possible repetition
"""
local_disjn = disjn.copy()
for elem in local_disjn:
for dis_elem in local_disjn:
if set(elem.disjunctions_req) == set(dis_elem.disjunctions_req) and \
elem.disjunction_id != dis_elem.disjunction_id:
local_disjn.remove(dis_elem)
return local_disjn
def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist):
""" use a list but a dictionnary might be helpful to find path based on request_id
TODO change all these req, dsjct, res lists into dict !
"""
path_res_list = []
reversed_path_res_list = []
propagated_reversed_path_res_list = []
for i, pathreq in enumerate(pathreqlist):
# use the power specified in requests but might be different from the one
# specified for design the power is an optional parameter for requests
# definition if optional, use the one defines in eqt_config.json
print(f'request {pathreq.request_id}')
print(f'Computing path from {pathreq.source} to {pathreq.destination}')
# adding first node to be clearer on the output
print(f'with path constraint: {[pathreq.source] + pathreq.nodes_list}')
# pathlist[i] contains the whole path information for request i
# last element is a transciver and where the result of the propagation is
# recorded.
# Important Note: since transceivers attached to roadms are actually logical
# elements to simulate performance, several demands having the same destination
# may use the same transponder for the performance simulation. This is why
# we use deepcopy: to ensure that each propagation is recorded and not overwritten
total_path = deepcopy(pathlist[i])
print(f'Computed path (roadms):{[e.uid for e in total_path if isinstance(e, Roadm)]}')
# for debug
# print(f'{pathreq.baud_rate} {pathreq.power} {pathreq.spacing} {pathreq.nb_channel}')
if total_path:
if pathreq.baud_rate is not None:
# means that at this point the mode was entered/forced by user and thus a
# baud_rate was defined
total_path = propagate(total_path, pathreq, equipment)
temp_snr01nm = round(mean(total_path[-1].snr+lin2db(pathreq.baud_rate/(12.5e9))), 2)
if temp_snr01nm < pathreq.OSNR:
msg = f'\tWarning! Request {pathreq.request_id} computed path from' +\
f' {pathreq.source} to {pathreq.destination} does not pass with' +\
f' {pathreq.tsp_mode}\n\tcomputedSNR in 0.1nm = {temp_snr01nm} ' +\
f'- required osnr {pathreq.OSNR}'
print(msg)
LOGGER.warning(msg)
pathreq.blocking_reason = 'MODE_NOT_FEASIBLE'
else:
total_path, mode = propagate_and_optimize_mode(total_path, pathreq, equipment)
# if no baudrate satisfies spacing, no mode is returned and the last explored mode
# a warning is shown in the propagate_and_optimize_mode
# propagate_and_optimize_mode function returns the mode with the highest bitrate
# that passes. if no mode passes, then a attribute blocking_reason is added on
# pathreq that contains the reason for blocking: 'NO_PATH', 'NO_FEASIBLE_MODE', ...
try:
if pathreq.blocking_reason in BLOCKING_NOPATH:
total_path = []
elif pathreq.blocking_reason in BLOCKING_NOMODE:
pathreq.baud_rate = mode['baud_rate']
pathreq.tsp_mode = mode['format']
pathreq.format = mode['format']
pathreq.OSNR = mode['OSNR']
pathreq.tx_osnr = mode['tx_osnr']
pathreq.bit_rate = mode['bit_rate']
# other blocking reason should not appear at this point
except AttributeError:
pathreq.baud_rate = mode['baud_rate']
pathreq.tsp_mode = mode['format']
pathreq.format = mode['format']
pathreq.OSNR = mode['OSNR']
pathreq.tx_osnr = mode['tx_osnr']
pathreq.bit_rate = mode['bit_rate']
# reversed path is needed for correct spectrum assignment
reversed_path = find_reversed_path(pathlist[i])
if pathreq.bidir:
# only propagate if bidir is true, but needs the reversed path anyway for
# correct spectrum assignment
rev_p = deepcopy(reversed_path)
print(f'\n\tPropagating Z to A direction {pathreq.destination} to {pathreq.source}')
print(f'\tPath (roadsm) {[r.uid for r in rev_p if isinstance(r,Roadm)]}\n')
propagated_reversed_path = propagate(rev_p, pathreq, equipment)
temp_snr01nm = round(mean(propagated_reversed_path[-1].snr +\
lin2db(pathreq.baud_rate/(12.5e9))), 2)
if temp_snr01nm < pathreq.OSNR:
msg = f'\tWarning! Request {pathreq.request_id} computed path from' +\
f' {pathreq.source} to {pathreq.destination} does not pass with' +\
f' {pathreq.tsp_mode}\n' +\
f'\tcomputedSNR in 0.1nm = {temp_snr01nm} - required osnr {pathreq.OSNR}'
print(msg)
LOGGER.warning(msg)
# TODO selection of mode should also be on reversed direction !!
pathreq.blocking_reason = 'MODE_NOT_FEASIBLE'
else:
propagated_reversed_path = []
else:
msg = 'Total path is empty. No propagation'
print(msg)
LOGGER.info(msg)
reversed_path = []
propagated_reversed_path = []
path_res_list.append(total_path)
reversed_path_res_list.append(reversed_path)
propagated_reversed_path_res_list.append(propagated_reversed_path)
# print to have a nice output
print('')
return path_res_list, reversed_path_res_list, propagated_reversed_path_res_list

View File

@@ -25,13 +25,12 @@ from copy import deepcopy
from gnpy.core.utils import automatic_nch, lin2db
from gnpy.core.network import build_network
from gnpy.core.exceptions import ServiceError
from gnpy.topology.request import (jsontocsv, requests_aggregation, compute_path_dsjctn,
ResultElement, PathRequest)
from gnpy.topology.request import (jsontocsv, requests_aggregation, compute_path_dsjctn, deduplicate_disjunctions,
compute_path_with_disjunction, ResultElement, PathRequest)
from gnpy.topology.spectrum_assignment import build_oms_list, pth_assign_spectrum
from gnpy.tools.convert import convert_file
from gnpy.tools.json_io import load_network, save_network, load_equipment, requests_from_json, disjunctions_from_json
from gnpy.tools.service_sheet import convert_service_sheet, correct_xls_route_list
from examples.path_requests_run import correct_disjn, compute_path_with_disjunction
TEST_DIR = Path(__file__).parent
DATA_DIR = TEST_DIR / 'data'
@@ -309,7 +308,7 @@ def test_json_response_generation(xls_input, expected_response_file):
oms_list = build_oms_list(network, equipment)
rqs = requests_from_json(data, equipment)
dsjn = disjunctions_from_json(data)
dsjn = correct_disjn(dsjn)
dsjn = deduplicate_disjunctions(dsjn)
rqs, dsjn = requests_aggregation(rqs, dsjn)
pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
propagatedpths, reversed_pths, reversed_propagatedpths = \

View File

@@ -19,11 +19,10 @@ from gnpy.core.network import build_network
from gnpy.core.utils import lin2db, automatic_nch
from gnpy.core.elements import Roadm, Transceiver
from gnpy.core.exceptions import SpectrumError
from gnpy.topology.request import compute_path_dsjctn, find_reversed_path
from gnpy.topology.request import compute_path_dsjctn, find_reversed_path, deduplicate_disjunctions
from gnpy.topology.spectrum_assignment import (build_oms_list, align_grids, nvalue_to_frequency,
bitmap_sum, Bitmap, spectrum_selection, pth_assign_spectrum)
from gnpy.tools.json_io import load_equipment, load_network, requests_from_json, disjunctions_from_json
from examples.path_requests_run import correct_disjn
TEST_DIR = Path(__file__).parent
DATA_DIR = TEST_DIR / 'data'
@@ -275,7 +274,7 @@ def test_reversed_direction(equipment, setup, requests, services):
"""
network, oms_list = setup
dsjn = disjunctions_from_json(services)
dsjn = correct_disjn(dsjn)
dsjn = deduplicate_disjunctions(dsjn)
paths = compute_path_dsjctn(network, equipment, requests, dsjn)
rev_pths = []
for pth in paths: