improving path_request_run robustness to wrong excel input

- adding a test to check that transponder types and modes are part of the eqpt library
- adding a formating on numeical value input for path request ids
- automatically printing the results into a csv file in addition to the json file
  and with the saime name+.csv
- printing results in a more pretty way for demo

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
This commit is contained in:
EstherLerouzic
2018-09-11 11:09:28 +01:00
parent 008a88192c
commit 3ade885e41
4 changed files with 236 additions and 127 deletions

View File

@@ -53,7 +53,16 @@ class Element:
class Request_element(Element):
def __init__(self,Request,eqpt_filename):
self.request_id = int(Request.request_id)
# request_id is str
# excel has automatic number formatting that adds .0 on integer values
# the next lines recover the pure int value, assuming this .0 is unwanted
if not isinstance(Request.request_id,str):
value = str(int(Request.request_id))
if value.endswith('.0'):
value = value[:-2]
self.request_id = value
else:
self.request_id = Request.request_id
self.source = Request.source
self.destination = Request.destination
self.srctpid = f'trx {Request.source}'
@@ -61,21 +70,27 @@ class Request_element(Element):
# test that trx_type belongs to eqpt_config.json
# if not replace it with a default
equipment = load_equipment(eqpt_filename)
if equipment['Transceiver'][Request.trx_type]:
self.trx_type = Request.trx_type
self.mode = Request.mode
else:
#TODO : this case must raise an error instead of using Voyager
self.trx_type = 'Voyager_16QAM'
print(f'Transceiver type {Request.trx_type} is not defined in {eqpt_filename}')
print('replaced by Voyager_16QAM')
try :
if equipment['Transceiver'][Request.trx_type]:
self.trx_type = Request.trx_type
if [mode for mode in equipment['Transceiver'][Request.trx_type].mode]:
self.mode = Request.mode
except KeyError:
msg = f'could not find tsp : {Request.trx_type} with mode: {Request.mode} in eqpt library \nComputation stopped.'
#print(msg)
logger.critical(msg)
exit()
# excel input are in GHz and dBm
self.spacing = Request.spacing * 1e9
self.power = db2lin(Request.power) * 1e-3
self.nb_channel = int(Request.nb_channel)
if isinstance(Request.disjoint_from,str):
self.disjoint_from = [int(n) for n in Request.disjoint_from.split()]
if not isinstance(Request.disjoint_from,str):
value = str(int(Request.disjoint_from))
if value.endswith('.0'):
value = value[:-2]
else:
self.disjoint_from = [int(Request.disjoint_from)]
value = Request.disjoint_from
self.disjoint_from = [n for n in value.split()]
self.nodes_list = []
if Request.nodes_list :
self.nodes_list = Request.nodes_list.split(' | ')

View File

@@ -30,7 +30,7 @@ from gnpy.core.network import load_network, build_network, set_roadm_loss
from gnpy.core.equipment import load_equipment, trx_mode_params
from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused
from gnpy.core.utils import db2lin, lin2db
from gnpy.core.request import Path_request, Result_element, compute_constrained_path, propagate
from gnpy.core.request import Path_request, Result_element, compute_constrained_path, propagate, jsontocsv
from copy import copy, deepcopy
#EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json'
@@ -138,17 +138,31 @@ if __name__ == '__main__':
test = compute_path(network, equipment, pths)
#TODO write results
print("demand\tsnr@bandwidth\tsnr@0.1nm")
header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR']
data = []
data.append(header)
for i, p in enumerate(test):
if p:
print(f'{pths[i].source} to {pths[i].destination} : \t{round(mean(p[-1].snr),2)} ,\
\t{round(mean(p[-1].snr+lin2db(pths[i].baud_rate/(12.5e9))),2)}')
line = [f'{pths[i].source} to {pths[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\
f'{round(mean(p[-1].snr+lin2db(pths[i].baud_rate/(12.5e9))),2)}',\
f'{pths[i].OSNR}']
else:
print(f'no path from {pths[i].source} to {pths[i].destination} ')
line = [f'no path from {pths[i].source} to {pths[i].destination} ']
data.append(line)
col_width = max(len(word) for row in data for word in row) # padding
for row in data:
print(''.join(word.ljust(col_width) for word in row))
if args.output :
result = []
for p in test:
result.append(Result_element(pths[test.index(p)],p))
with open(args.output, 'w') as f:
f.write(dumps(path_result_json(result), indent=2))
f.write(dumps(path_result_json(result), indent=2))
fnamecsv = next(s for s in args.output.split('.')) + '.csv'
with open(fnamecsv,"w") as fcsv :
jsontocsv(path_result_json(result),equipment,fcsv)

View File

@@ -14,15 +14,11 @@ and write results in an CSV file
"""
from sys import exit
from csv import writer
from argparse import ArgumentParser
from pathlib import Path
from json import dumps, loads
from json import loads
from gnpy.core.equipment import load_equipment
from gnpy.core.utils import lin2db
START_LINE = 5
from gnpy.core.request import jsontocsv
parser = ArgumentParser(description = 'A function that writes json path results in an excel sheet.')
@@ -30,65 +26,14 @@ parser.add_argument('filename', nargs='?', type = Path)
parser.add_argument('output_filename', nargs='?', type = Path)
parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json')
if __name__ == '__main__':
args = parser.parse_args()
print(f'coucou {args.output_filename}')
with open(args.output_filename,"w") as file :
mywriter = writer(file)
mywriter.writerow(('path-id','source','destination','transponder-type',\
'transponder-mode','baud rate (Gbaud)', 'input power (dBm)','path','OSNR@bandwidth','OSNR@0.1nm','SNR@bandwidth','SNR@0.1nm','Pass?'))
with open(args.filename) as f:
print(f'Reading {args.filename}')
json_data = loads(f.read())
equipment = load_equipment(args.eqpt_filename)
tspjsondata = equipment['Transceiver']
#print(tspjsondata)
for p in json_data['path']:
path_id = int(p['path-id'])
source = p['path-properties']['path-route-objects'][0]\
['path-route-object']['unnumbered-hop']['node-id']
destination = p['path-properties']['path-route-objects'][-1]\
['path-route-object']['unnumbered-hop']['node-id']
pth = ' | '.join([ e['path-route-object']['unnumbered-hop']['node-id']
for e in p['path-properties']['path-route-objects']])
print(f'Writing in {args.output_filename}')
jsontocsv(json_data,equipment,file)
[tsp,mode] = p['path-properties']['path-route-objects'][0]\
['path-route-object']['unnumbered-hop']['hop-type'].split(' - ')
# find the min acceptable OSNR, baud rate from the eqpt library based on tsp (tupe) and mode (format)
try:
[minosnr, baud_rate] = next([m['OSNR'] , m['baud_rate']]
for m in equipment['Transceiver'][tsp].mode if m['format']==mode)
# for debug
# print(f'coucou {baud_rate}')
except IndexError:
msg = f'could not find tsp : {self.tsp} with mode: {self.tsp_mode} in eqpt library'
raise ValueError(msg)
output_snr = next(e['accumulative-value']
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@0.1nm')
output_snrbandwidth = next(e['accumulative-value']
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@bandwidth')
output_osnr = next(e['accumulative-value']
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@0.1nm')
output_osnrbandwidth = next(e['accumulative-value']
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@bandwidth')
power = next(e['accumulative-value']
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'reference_power')
mywriter.writerow((path_id,
source,
destination,
tsp,
mode,
baud_rate*1e-9,
round(lin2db(power)+30,2),
pth,
output_osnrbandwidth,
output_osnr,
output_snrbandwidth,
output_snr,
output_snr >= minosnr
))

View File

@@ -25,6 +25,7 @@ from gnpy.core.network import set_roadm_loss
from gnpy.core.utils import db2lin, lin2db
from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power
from copy import copy, deepcopy
from csv import writer
logger = getLogger(__name__)
@@ -70,7 +71,7 @@ class Path_request:
class Result_element(Element):
def __init__(self,path_request,computed_path):
self.path_id = int(path_request.request_id)
self.path_id = path_request.request_id
self.path_request = path_request
self.computed_path = computed_path
hop_type = []
@@ -83,56 +84,125 @@ class Result_element(Element):
uid = property(lambda self: repr(self))
@property
def pathresult(self):
return {
'path-id': self.path_id,
'path-properties':{
'path-metric': [
{
'metric-type': 'SNR@bandwidth',
'accumulative-value': round(mean(self.computed_path[-1].snr),2)
},
{
'metric-type': 'SNR@0.1nm',
'accumulative-value': round(mean(self.computed_path[-1].snr+lin2db(self.path_request.baud_rate/12.5e9)),2)
},
{
'metric-type': 'OSNR@bandwidth',
'accumulative-value': round(mean(self.computed_path[-1].osnr_ase),2)
},
{
'metric-type': 'OSNR@0.1nm',
'accumulative-value': round(mean(self.computed_path[-1].osnr_ase_01nm),2)
},
{
'metric-type': 'reference_power',
'accumulative-value': self.path_request.power
}
],
'path-srlgs': {
'usage': 'not used yet',
'values': 'not used yet'
},
'path-route-objects': [
{
'path-route-object': {
'index': self.computed_path.index(n),
'unnumbered-hop': {
'node-id': n.uid,
'link-tp-id': n.uid,
'hop-type': self.hop_type[self.computed_path.index(n)],
'direction': 'not used'
if not self.computed_path:
return {
'path-id': self.path_id,
'path-properties':{
'path-metric': [
{
'metric-type': 'SNR@bandwidth',
'accumulative-value': 'None'
},
{
'metric-type': 'SNR@0.1nm',
'accumulative-value': 'None'
},
{
'metric-type': 'OSNR@bandwidth',
'accumulative-value': 'None'
},
{
'metric-type': 'OSNR@0.1nm',
'accumulative-value': 'None'
},
{
'metric-type': 'reference_power',
'accumulative-value': self.path_request.power
}
],
'path-srlgs': {
'usage': 'not used yet',
'values': 'not used yet'
},
'path-route-objects': [
{
'path-route-object': {
'index': 0,
'unnumbered-hop': {
'node-id': self.path_request.source,
'link-tp-id': self.path_request.source,
'hop-type': ' - '.join([self.path_request.tsp, self.path_request.tsp_mode]),
'direction': 'not used'
},
'label-hop': {
'te-label': {
'generic': 'not used yet',
'direction': 'not used yet'
}
}
}
},
'label-hop': {
'te-label': {
'generic': 'not used yet',
'direction': 'not used yet'
{
'path-route-object': {
'index': 1,
'unnumbered-hop': {
'node-id': self.path_request.destination,
'link-tp-id': self.path_request.destination,
'hop-type': ' - '.join([self.path_request.tsp, self.path_request.tsp_mode]),
'direction': 'not used'
},
'label-hop': {
'te-label': {
'generic': 'not used yet',
'direction': 'not used yet'
}
}
}
}
} for n in self.computed_path
]
]
}
}
else:
return {
'path-id': self.path_id,
'path-properties':{
'path-metric': [
{
'metric-type': 'SNR@bandwidth',
'accumulative-value': round(mean(self.computed_path[-1].snr),2)
},
{
'metric-type': 'SNR@0.1nm',
'accumulative-value': round(mean(self.computed_path[-1].snr+lin2db(self.path_request.baud_rate/12.5e9)),2)
},
{
'metric-type': 'OSNR@bandwidth',
'accumulative-value': round(mean(self.computed_path[-1].osnr_ase),2)
},
{
'metric-type': 'OSNR@0.1nm',
'accumulative-value': round(mean(self.computed_path[-1].osnr_ase_01nm),2)
},
{
'metric-type': 'reference_power',
'accumulative-value': self.path_request.power
}
],
'path-srlgs': {
'usage': 'not used yet',
'values': 'not used yet'
},
'path-route-objects': [
{
'path-route-object': {
'index': self.computed_path.index(n),
'unnumbered-hop': {
'node-id': n.uid,
'link-tp-id': n.uid,
'hop-type': self.hop_type[self.computed_path.index(n)],
'direction': 'not used'
},
'label-hop': {
'te-label': {
'generic': 'not used yet',
'direction': 'not used yet'
}
}
}
} for n in self.computed_path
]
}
}
}
@property
def json(self):
@@ -192,4 +262,69 @@ def propagate(path, req, equipment, show=False):
si = el(si)
if show :
print(el)
return path
return path
def jsontocsv(json_data,equipment,fileout):
# read json path result file in accordance with:
# Yang model for requesting Path Computation
# draft-ietf-teas-yang-path-computation-01.txt.
# and write results in an CSV file
mywriter = writer(fileout)
mywriter.writerow(('path-id','source','destination','transponder-type',\
'transponder-mode','baud rate (Gbaud)', 'input power (dBm)','path',\
'OSNR@bandwidth','OSNR@0.1nm','SNR@bandwidth','SNR@0.1nm','Pass?'))
tspjsondata = equipment['Transceiver']
#print(tspjsondata)
for p in json_data['path']:
path_id = p['path-id']
source = p['path-properties']['path-route-objects'][0]\
['path-route-object']['unnumbered-hop']['node-id']
destination = p['path-properties']['path-route-objects'][-1]\
['path-route-object']['unnumbered-hop']['node-id']
pth = ' | '.join([ e['path-route-object']['unnumbered-hop']['node-id']
for e in p['path-properties']['path-route-objects']])
[tsp,mode] = p['path-properties']['path-route-objects'][0]\
['path-route-object']['unnumbered-hop']['hop-type'].split(' - ')
# find the min acceptable OSNR, baud rate from the eqpt library based on tsp (tupe) and mode (format)
try:
[minosnr, baud_rate] = next([m['OSNR'] , m['baud_rate']]
for m in equipment['Transceiver'][tsp].mode if m['format']==mode)
# for debug
# print(f'coucou {baud_rate}')
except IndexError:
msg = f'could not find tsp : {self.tsp} with mode: {self.tsp_mode} in eqpt library'
raise ValueError(msg)
output_snr = next(e['accumulative-value']
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@0.1nm')
output_snrbandwidth = next(e['accumulative-value']
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@bandwidth')
output_osnr = next(e['accumulative-value']
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@0.1nm')
output_osnrbandwidth = next(e['accumulative-value']
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@bandwidth')
power = next(e['accumulative-value']
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'reference_power')
if isinstance(output_snr, str):
isok = ''
else:
isok = output_snr >= minosnr
mywriter.writerow((path_id,
source,
destination,
tsp,
mode,
baud_rate*1e-9,
round(lin2db(power)+30,2),
pth,
output_osnrbandwidth,
output_osnr,
output_snrbandwidth,
output_snr,
isok
))