From a6087ce35431706dc5331a8f314afc25257005a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= Date: Fri, 27 Sep 2019 09:22:08 +0200 Subject: [PATCH 01/35] Highlight OSNR @ 0.1nm Esther and Jonas pointed out that it is much more common to work with OSNR measurements per 0.1nm rather than per signal bandwidth. This is in line with what OSAs usually report and what transponder datasheets specify. cc #307 --- examples/transmission_main_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/transmission_main_example.py b/examples/transmission_main_example.py index 340b3e07f..b3eb3a44b 100755 --- a/examples/transmission_main_example.py +++ b/examples/transmission_main_example.py @@ -161,7 +161,7 @@ def main(network, equipment, source, destination, sim_params, req=None): print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f} dBm:') else: print(f'\nTransmission results:') - print(f' Final SNR total (signal bw): {ansi_escapes.cyan}{mean(destination.snr):.02f} dB{ansi_escapes.reset}') + print(f' Final SNR total (0.1 nm): {ansi_escapes.cyan}{mean(destination.snr_01nm):.02f} dB{ansi_escapes.reset}') #print(f'\n !!!!!!!!!!!!!!!!! TEST POINT !!!!!!!!!!!!!!!!!!!!!') #print(f'carriers ase output of {path[1]} =\n {list(path[1].carriers("out", "nli"))}') From 3df27fe31547db45806201ea91dd8701878dd999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= Date: Tue, 1 Oct 2019 15:21:34 +0200 Subject: [PATCH 02/35] Print both ASE-OSNR and final GSNR for both 0.1nm and signal-bw in power sweep As agreed upon during today's coders call, this is something that is needed by actual users in the field, so let's prioritize their needs over clarity of output for demo purposes. --- examples/transmission_main_example.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/transmission_main_example.py b/examples/transmission_main_example.py index b3eb3a44b..585aaf9ee 100755 --- a/examples/transmission_main_example.py +++ b/examples/transmission_main_example.py @@ -157,6 +157,8 @@ def main(network, equipment, source, destination, sim_params, req=None): if len(power_range) == 1: for elem in path: print(elem) + else: + print(path[-1]) if power_mode: print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f} dBm:') else: From 8c3b514f904019a3d0afbfc88d2f74cad2a1e488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= Date: Tue, 1 Oct 2019 15:24:40 +0200 Subject: [PATCH 03/35] Only print out "pretty summary" when not doing the power sweep --- examples/transmission_main_example.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/transmission_main_example.py b/examples/transmission_main_example.py index 585aaf9ee..82d13bc10 100755 --- a/examples/transmission_main_example.py +++ b/examples/transmission_main_example.py @@ -157,13 +157,13 @@ def main(network, equipment, source, destination, sim_params, req=None): if len(power_range) == 1: for elem in path: print(elem) + if power_mode: + print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f} dBm:') + else: + print(f'\nTransmission results:') + print(f' Final SNR total (0.1 nm): {ansi_escapes.cyan}{mean(destination.snr_01nm):.02f} dB{ansi_escapes.reset}') else: print(path[-1]) - if power_mode: - print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f} dBm:') - else: - print(f'\nTransmission results:') - print(f' Final SNR total (0.1 nm): {ansi_escapes.cyan}{mean(destination.snr_01nm):.02f} dB{ansi_escapes.reset}') #print(f'\n !!!!!!!!!!!!!!!!! TEST POINT !!!!!!!!!!!!!!!!!!!!!') #print(f'carriers ase output of {path[1]} =\n {list(path[1].carriers("out", "nli"))}') From 8d31d924f20f41d2d74d4dd5aa9669cb401159d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= Date: Sun, 6 Oct 2019 20:37:24 +0200 Subject: [PATCH 04/35] Remove unused function --- gnpy/core/elements.py | 2 +- gnpy/core/utils.py | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/gnpy/core/elements.py b/gnpy/core/elements.py index d7215a9f3..5bece0aa1 100644 --- a/gnpy/core/elements.py +++ b/gnpy/core/elements.py @@ -25,7 +25,7 @@ from gnpy.core.node import Node from gnpy.core.units import UNITS -from gnpy.core.utils import lin2db, db2lin, itufs, itufl, snr_sum +from gnpy.core.utils import lin2db, db2lin, itufl, snr_sum from gnpy.core.science_utils import propagate_raman_fiber, _psi class Transceiver(Node): diff --git a/gnpy/core/utils.py b/gnpy/core/utils.py index 1a1951712..0d301c48e 100644 --- a/gnpy/core/utils.py +++ b/gnpy/core/utils.py @@ -73,21 +73,6 @@ def c(): return constants.c -def itufs(spacing, startf=191.35, stopf=196.10): - """Creates an array of frequencies whose default range is - 191.35-196.10 THz - - :param spacing: Frequency spacing in THz - :param starf: Start frequency in THz - :param stopf: Stop frequency in THz - :type spacing: float - :type startf: float - :type stopf: float - :return an array of frequnecies determined by the spacing parameter - :rtype: numpy.ndarray - """ - return np.arange(startf, stopf + spacing / 2, spacing) - def itufl(length, startf=191.35, stopf=196.10): """Creates an array of frequencies whose default range is 191.35-196.10 THz From a27ad5722081cf1fa2b428b512813d02e496cebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= Date: Sun, 6 Oct 2019 20:38:31 +0200 Subject: [PATCH 05/35] Do not use confusing default values This looks like the root cause of bug #243, the default values suggested that this function works in THz. These defaults are not used anywhere, so let's get rid of something which adds no value and actively confuses people. --- gnpy/core/utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gnpy/core/utils.py b/gnpy/core/utils.py index 0d301c48e..c5984bd83 100644 --- a/gnpy/core/utils.py +++ b/gnpy/core/utils.py @@ -73,9 +73,8 @@ def c(): return constants.c -def itufl(length, startf=191.35, stopf=196.10): - """Creates an array of frequencies whose default range is - 191.35-196.10 THz +def itufl(length, startf, stopf): + """Create an array of frequencies :param length: number of elements :param starf: Start frequency in THz From 029bac4b03e93d1dcfc1e2114aa07cb044aa1e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= Date: Sun, 6 Oct 2019 20:43:50 +0200 Subject: [PATCH 06/35] utils: more descriptive name for `itufl` This might have nothing to do with the ITU frequency grid (it's really just about a uniform distribution), so let's give it a more readable name and more readable parameters. --- gnpy/core/elements.py | 4 ++-- gnpy/core/utils.py | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/gnpy/core/elements.py b/gnpy/core/elements.py index 5bece0aa1..f5678f177 100644 --- a/gnpy/core/elements.py +++ b/gnpy/core/elements.py @@ -25,7 +25,7 @@ from gnpy.core.node import Node from gnpy.core.units import UNITS -from gnpy.core.utils import lin2db, db2lin, itufl, snr_sum +from gnpy.core.utils import lin2db, db2lin, arrange_frequencies, snr_sum from gnpy.core.science_utils import propagate_raman_fiber, _psi class Transceiver(Node): @@ -615,7 +615,7 @@ def interpol_params(self, frequencies, pin, baud_rates, pref): self.channel_freq, self.nf, self.interpol_dgt and self.interpol_gain_ripple """ # TODO|jla: read amplifier actual frequencies from additional params in json - amplifier_freq = itufl(len(self.params.dgt), self.params.f_min, self.params.f_max) # Hz + amplifier_freq = arrange_frequencies(len(self.params.dgt), self.params.f_min, self.params.f_max) # Hz self.channel_freq = frequencies self.interpol_dgt = interp(self.channel_freq, amplifier_freq, self.params.dgt) diff --git a/gnpy/core/utils.py b/gnpy/core/utils.py index c5984bd83..fd1d92aaf 100644 --- a/gnpy/core/utils.py +++ b/gnpy/core/utils.py @@ -73,19 +73,19 @@ def c(): return constants.c -def itufl(length, startf, stopf): +def arrange_frequencies(length, start, stop): """Create an array of frequencies :param length: number of elements - :param starf: Start frequency in THz - :param stopf: Stop frequency in THz + :param star: Start frequency in THz + :param stop: Stop frequency in THz :type length: integer - :type startf: float - :type stopf: float - :return an array of frequnecies determined by the spacing parameter + :type start: float + :type stop: float + :return an array of frequencies determined by the spacing parameter :rtype: numpy.ndarray """ - return np.linspace(startf, stopf, length) + return np.linspace(start, stop, length) def h(): """ From a63a6ac0ec71f1d603aece6d542190bc130b3b81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= Date: Sun, 6 Oct 2019 23:12:09 +0200 Subject: [PATCH 07/35] codacy: do not complain about asserts in the test suite Writing an explicit comparison followed by argument printing *and* handling the final error is just backwards. Let the tool be quiet, this nonsense does not really add any value. --- .bandit | 1 + 1 file changed, 1 insertion(+) create mode 100644 .bandit diff --git a/.bandit b/.bandit new file mode 100644 index 000000000..75d550c32 --- /dev/null +++ b/.bandit @@ -0,0 +1 @@ +skips: ['B101'] From fea2b84bb940a1cbd9bcedda1e45e76f5cea0ea0 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 19 Aug 2019 11:46:34 +0100 Subject: [PATCH 08/35] correction of the json generation for path computation - changes concerning names due to ietf path-computation draft changes: - 'unnumbered-hop' changed to 'num-unnum-hop' - suppression of 'label-hop' - 'link-diverse' and 'node-diverse' changed to 'disjointness' - correction because of previously wrong interpretation of the model: - detailed succession on include nodes moved from 'optimisation /explicit-route-include-objects' to 'route-object-include-exclude' in 'explicit-route-objects' attribute - correction of keywords - n and m replaced by N and M - strict and loose replaces by STRICT and LOOSE strings - change the name of respponse from 'path' to 'response' example service file corrected accordingly Signed-off-by: EstherLerouzic --- examples/meshTopologyExampleV2_services.json | 94 ++++++-------------- examples/path_requests_run.py | 4 +- gnpy/core/service_sheet.py | 12 +-- 3 files changed, 36 insertions(+), 74 deletions(-) diff --git a/examples/meshTopologyExampleV2_services.json b/examples/meshTopologyExampleV2_services.json index cdce3cc0e..91e4d2a84 100644 --- a/examples/meshTopologyExampleV2_services.json +++ b/examples/meshTopologyExampleV2_services.json @@ -13,8 +13,8 @@ "trx_mode": null, "effective-freq-slot": [ { - "n": "null", - "m": "null" + "N": "null", + "M": "null" } ], "spacing": 50000000000.0, @@ -22,9 +22,6 @@ "output-power": 0.0012589254117941673, "path_bandwidth": 100000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -40,8 +37,8 @@ "trx_mode": "mode 1", "effective-freq-slot": [ { - "n": "null", - "m": "null" + "N": "null", + "M": "null" } ], "spacing": 50000000000.0, @@ -50,66 +47,42 @@ "path_bandwidth": 200000000000.0 } }, - "optimizations": { - "explicit-route-include-objects": [ + "explicit-route-objects": { + "route-object-include-exclude": [ { + "explicit-route-usage": "route-include-ero", "index": 0, - "unnumbered-hop": { + "num-unnum-hop": { "node-id": "roadm Brest_KLA", "link-tp-id": "link-tp-id is not used", - "hop-type": "loose", - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } + "hop-type": "LOOSE" } }, { + "explicit-route-usage": "route-include-ero", "index": 1, - "unnumbered-hop": { + "num-unnum-hop": { "node-id": "roadm Lannion_CAS", "link-tp-id": "link-tp-id is not used", - "hop-type": "loose", - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } + "hop-type": "LOOSE" } }, { + "explicit-route-usage": "route-include-ero", "index": 2, - "unnumbered-hop": { + "num-unnum-hop": { "node-id": "roadm Lorient_KMA", "link-tp-id": "link-tp-id is not used", - "hop-type": "loose", - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } + "hop-type": "LOOSE" } }, { + "explicit-route-usage": "route-include-ero", "index": 3, - "unnumbered-hop": { + "num-unnum-hop": { "node-id": "roadm Vannes_KBE", "link-tp-id": "link-tp-id is not used", - "hop-type": "loose", - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } + "hop-type": "LOOSE" } } ] @@ -128,8 +101,8 @@ "trx_mode": "mode 1", "effective-freq-slot": [ { - "n": "null", - "m": "null" + "N": "null", + "M": "null" } ], "spacing": 50000000000.0, @@ -137,9 +110,6 @@ "output-power": null, "path_bandwidth": 60000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -155,8 +125,8 @@ "trx_mode": null, "effective-freq-slot": [ { - "n": "null", - "m": "null" + "N": "null", + "M": "null" } ], "spacing": 75000000000.0, @@ -164,9 +134,6 @@ "output-power": 0.0019952623149688794, "path_bandwidth": 150000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -182,8 +149,8 @@ "trx_mode": "mode 2", "effective-freq-slot": [ { - "n": "null", - "m": "null" + "N": "null", + "M": "null" } ], "spacing": 75000000000.0, @@ -191,9 +158,6 @@ "output-power": 0.0019952623149688794, "path_bandwidth": 20000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -282,9 +246,8 @@ { "synchronization-id": "3", "svec": { - "relaxable": "False", - "link-diverse": "True", - "node-diverse": "True", + "relaxable": "false", + "disjointness": "node link", "request-id-number": [ "3", "1" @@ -294,9 +257,8 @@ { "synchronization-id": "4", "svec": { - "relaxable": "False", - "link-diverse": "True", - "node-diverse": "True", + "relaxable": "false", + "disjointness": "node link", "request-id-number": [ "4", "5" diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 4b836e8a1..e7a3e83df 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -216,7 +216,7 @@ def correct_route_list(network, pathreqlist): if n_id not in anytype : nodes_suggestion = [uid for uid in anytype \ if n_id.lower() in uid.lower()] - if pathreq.loose_list[i] == 'loose': + if pathreq.loose_list[i] == 'LOOSE': if len(nodes_suggestion)>0 : new_n = nodes_suggestion[0] print(f'invalid route node specified:\ @@ -257,7 +257,7 @@ def correct_disjn(disjn): def path_result_json(pathresult): data = { - 'path': [n.json for n in pathresult] + 'response': [n.json for n in pathresult] } return data diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index 8b2ac1b72..b3b49b8e4 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -120,9 +120,9 @@ def __init__(self,Request,eqpt_filename): # the excel parser applies the same hop-type to all nodes in the route nodes_list. # user can change this per node in the generated json - self.loose = 'loose' + self.loose = 'LOOSE' if Request.is_loose == 'no' : - self.loose = 'strict' + self.loose = 'STRICT' self.path_bandwidth = None if Request.path_bandwidth is not None: self.path_bandwidth = Request.path_bandwidth * 1e9 @@ -132,6 +132,7 @@ def __init__(self,Request,eqpt_filename): uid = property(lambda self: repr(self)) @property def pathrequest(self): + req_dictionnary = { 'request-id':self.request_id, 'source': self.source, @@ -143,7 +144,7 @@ def pathrequest(self): 'technology': 'flexi-grid', 'trx_type' : self.trx_type, 'trx_mode' : self.mode, - 'effective-freq-slot':[{'n': 'null','m': 'null'}] , + 'effective-freq-slot':[{'N': 'null','M': 'null'}] , 'spacing' : self.spacing, 'max-nb-of-channel' : self.nb_channel, 'output-power' : self.power @@ -181,9 +182,8 @@ def pathsync(self): if self.disjoint_from : return {'synchronization-id':self.request_id, 'svec': { - 'relaxable' : 'False', - 'link-diverse': 'True', - 'node-diverse': 'True', + 'relaxable' : 'false', + 'disjointness': 'node link', 'request-id-number': [self.request_id]+ [n for n in self.disjoint_from] } } From 8f424e8c9d821f781257e826cbe5bcc654dbb104 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 19 Aug 2019 11:50:45 +0100 Subject: [PATCH 09/35] correction of json generation: removing unused objects - removing empty objects (if no content , removes the object), especially - removing empty synchronization vector if no disjunction constraint exists - removing optional power and nb of channel fields, if user did not specify any when reading, first try if the object exists : try:/except to catch this case Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 21 ++++++++++++--------- gnpy/core/service_sheet.py | 18 +++++++++++++----- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index e7a3e83df..72281db67 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -122,15 +122,18 @@ def consistency_check(params, f_max_from_si): def disjunctions_from_json(json_data): disjunctions_list = [] - - for snc in json_data['synchronization']: - params = {} - params['disjunction_id'] = snc['synchronization-id'] - params['relaxable'] = snc['svec']['relaxable'] - params['link_diverse'] = snc['svec']['link-diverse'] - params['node_diverse'] = snc['svec']['node-diverse'] - params['disjunctions_req'] = snc['svec']['request-id-number'] - disjunctions_list.append(Disjunction(**params)) + try: + for snc in json_data['synchronization']: + params = {} + params['disjunction_id'] = snc['synchronization-id'] + params['relaxable'] = snc['svec']['relaxable'] + params['link_diverse'] = 'link' in snc['svec']['disjointness'] + params['node_diverse'] = 'node' in snc['svec']['disjointness'] + params['disjunctions_req'] = snc['svec']['request-id-number'] + disjunctions_list.append(Disjunction(**params)) + print(disjunctions_list) + except KeyError: + pass return disjunctions_list diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index b3b49b8e4..dd1368621 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -187,6 +187,8 @@ def pathsync(self): 'request-id-number': [self.request_id]+ [n for n in self.disjoint_from] } } + else: + return None # TO-DO: avoid multiple entries with same synchronisation vectors @property def json(self): @@ -201,11 +203,17 @@ def convert_service_sheet(input_filename, eqpt_filename, output_filename='', fil output_filename = f'{str(input_filename)[0:len(str(input_filename))-len(str(input_filename.suffixes[0]))]}_services.json' # for debug # print(json_filename) - data = { - 'path-request': [n.json[0] for n in req], - 'synchronization': [n.json[1] for n in req - if n.json[1] is not None] - } + # if there is no sync vector , do not write any synchronization + synchro = [n.json[1] for n in req if n.json[1] is not None] + if synchro : + data = { + 'path-request': [n.json[0] for n in req], + 'synchronization': synchro + } + else: + data = { + 'path-request': [n.json[0] for n in req] + } with open(output_filename, 'w', encoding='utf-8') as f: f.write(dumps(data, indent=2, ensure_ascii=False)) return data From e5ec6694192d59bcc61443cf080dede706f23216 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 19 Aug 2019 11:40:51 +0100 Subject: [PATCH 10/35] applying changes in the request dict creation + removing (currently) unused direction attribute. This will be added again in a later PR when it will be used Signed-off-by: EstherLerouzic --- gnpy/core/service_sheet.py | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index dd1368621..5854b01c3 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -148,31 +148,24 @@ def pathrequest(self): 'spacing' : self.spacing, 'max-nb-of-channel' : self.nb_channel, 'output-power' : self.power - # 'path_bandwidth' : self.path_bandwidth } - }, - 'optimizations': { - 'explicit-route-include-objects': [ - { - 'index': self.nodes_list.index(node), - 'unnumbered-hop':{ - 'node-id': f'{node}', - 'link-tp-id': 'link-tp-id is not used', - 'hop-type': f'{self.loose}', - 'direction': 'direction is not used' - }, - 'label-hop':{ - 'te-label': { - 'generic': 'generic is not used', - 'direction': 'direction is not used' - } + } + } + + if self.nodes_list: + req_dictionnary['explicit-route-objects'] = {} + temp = {'route-object-include-exclude' : [ + {'explicit-route-usage': 'route-include-ero', + 'index': self.nodes_list.index(node), + 'num-unnum-hop': { + 'node-id': f'{node}', + 'link-tp-id': 'link-tp-id is not used', + 'hop-type': f'{self.loose}', } } - for node in self.nodes_list - ] - - } - } + for node in self.nodes_list ] + } + req_dictionnary['explicit-route-objects'] = temp if self.path_bandwidth is not None: req_dictionnary['path-constraints']['te-bandwidth']['path_bandwidth'] = self.path_bandwidth From 402155c225b8ce3974a25f59d246775620b20ec9 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 14 Aug 2019 17:55:01 +0100 Subject: [PATCH 11/35] change the 'no path' case answer in case no path could be computed, the answer is changed according to ietf path-computation model to a simple 'no-path' object Signed-off-by: EstherLerouzic --- gnpy/core/request.py | 72 +++----------------------------------------- 1 file changed, 4 insertions(+), 68 deletions(-) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index ffbf071a7..122aa8826 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -133,76 +133,12 @@ def __init__(self,path_request,computed_path): def pathresult(self): 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 - }, - { - 'metric-type': 'path_bandwidth', - 'accumulative-value': self.path_request.path_bandwidth - } - ], - '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': self.hop_type, - 'direction': 'not used' - }, - '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': self.hop_type, - 'direction': 'not used' - }, - 'label-hop': { - 'te-label': { - 'generic': 'not used yet', - 'direction': 'not used yet' - } - } - } + 'response-id': self.path_id, + 'no-path': "Response without path information, due to failure performing the path computation" + } } - ] + } } - } else: return { 'path-id': self.path_id, From d79d2e0724695d0ab76515c623865fd29b4933f1 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 14 Aug 2019 17:59:30 +0100 Subject: [PATCH 12/35] change the way transponder and mode are returned in the response previous way used a wrong interpretation of hop-type. in ietf model, hop-type is reserved for LOOSE or STRICT constraint description. Instead, transponder info , according to ietf usual way, should be included aas a new path-route-object type. such object is not yet defined in IETF so this is a GNPY proposal to use a 'transponder' object with type and mode attributes. this is what has been ecncoded in reques.py for requests and for answers Signed-off-by: EstherLerouzic --- gnpy/core/request.py | 69 +++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 39 deletions(-) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 122aa8826..5d70d0f8f 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -115,19 +115,6 @@ def __init__(self,path_request,computed_path): self.path_id = path_request.request_id self.path_request = path_request self.computed_path = computed_path - hop_type = [] - if len(computed_path)>0 : - for e in computed_path : - if isinstance(e, Transceiver) : - hop_type.append(' - '.join([path_request.tsp,path_request.tsp_mode])) - else: - hop_type.append('not recorded') - else: - # TODO differentiate empty path in case not feasible because of tsp or not feasible because - # ther is no path connecting the nodes (whatever the tsp) - mode = 'not feasible with this transponder' - hop_type = ' - '.join([path_request.tsp,mode]) - self.hop_type = hop_type uid = property(lambda self: repr(self)) @property def pathresult(self): @@ -136,12 +123,37 @@ def pathresult(self): 'response-id': self.path_id, 'no-path': "Response without path information, due to failure performing the path computation" } + else: + index = 0 + pro_list = [] + for n in self.computed_path : + temp = { + 'path-route-object': { + 'index': index, + 'num-unnum-hop': { + 'node-id': n.uid, + 'link-tp-id': n.uid, + # TODO change index in order to insert transponder attribute } } } - else: - return { - 'path-id': self.path_id, + pro_list.append(temp) + index += 1 + if isinstance(n, Transceiver) : + temp = { + 'path-route-object': { + 'index': index, + 'transponder' : { + 'transponder-type' : self.path_request.tsp, + 'transponder-mode' : self.path_request.tsp_mode, + } + } + } + pro_list.append(temp) + index += 1 + + response = { + 'response-id': self.path_id, 'path-properties':{ 'path-metric': [ { @@ -169,31 +181,10 @@ def pathresult(self): 'accumulative-value': self.path_request.path_bandwidth } ], - '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 - ] + 'path-route-objects': pro_list } } + return response @property def json(self): From e7084a2c29f0ca3b6d2728667aedda748c63c6a7 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 14 Aug 2019 18:09:59 +0100 Subject: [PATCH 13/35] correction to support empty vectors of constraints change the naming according to model update + previous changes authorize empty list of nodes. This new test supports this case Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 72281db67..08930dc14 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -60,11 +60,13 @@ def requests_from_json(json_data,equipment): params['trx_type'] = req['path-constraints']['te-bandwidth']['trx_type'] params['trx_mode'] = req['path-constraints']['te-bandwidth']['trx_mode'] params['format'] = params['trx_mode'] - nd_list = req['optimizations']['explicit-route-include-objects'] - params['nodes_list'] = [n['unnumbered-hop']['node-id'] for n in nd_list] - params['loose_list'] = [n['unnumbered-hop']['hop-type'] for n in nd_list] params['spacing'] = req['path-constraints']['te-bandwidth']['spacing'] - + try : + nd_list = req['explicit-route-objects']['route-object-include-exclude'] + except KeyError: + nd_list = [] + params['nodes_list'] = [n['num-unnum-hop']['node-id'] for n in nd_list] + params['loose_list'] = [n['num-unnum-hop']['hop-type'] for n in nd_list] # recover trx physical param (baudrate, ...) from type and mode # in trx_mode_params optical power is read from equipment['SI']['default'] and # nb_channel is computed based on min max frequency and spacing From c9d8282e7f291ab56ed51fd5442ea7f995103e11 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 14 Aug 2019 18:15:07 +0100 Subject: [PATCH 14/35] enable power and nch to be optional in requests definition since empty attributes have been removed from the requests with previous changes, some aditional tests are needed to continue supporting optional power and nch. (previous objects were created with null or default values) user may enter requests without specifying these fields, in this case the object 'output-power' or 'max-nb-of-channel' do not exist. the try /except form handles the corresponding exception and default values are kept else. Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 08930dc14..108c0b7f0 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -75,20 +75,24 @@ def requests_from_json(json_data,equipment): # print(trx_params['min_spacing']) # optical power might be set differently in the request. if it is indicated then the # params['power'] is updated - if req['path-constraints']['te-bandwidth']['output-power']: - params['power'] = req['path-constraints']['te-bandwidth']['output-power'] - + try: + if req['path-constraints']['te-bandwidth']['output-power']: + params['power'] = req['path-constraints']['te-bandwidth']['output-power'] + except KeyError: + pass # same process for nb-channel f_min = params['f_min'] f_max_from_si = params['f_max'] - if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None : - nch = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] - params['nb_channel'] = nch - spacing = params['spacing'] - params['f_max'] = f_min + nch*spacing - else : + try: + if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None: + nch = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] + params['nb_channel'] = nch + spacing = params['spacing'] + params['f_max'] = f_min + nch*spacing + else : + params['nb_channel'] = automatic_nch(f_min,f_max_from_si,params['spacing']) + except KeyError: params['nb_channel'] = automatic_nch(f_min,f_max_from_si,params['spacing']) - consistency_check(params, f_max_from_si) try : From 1e7c70a59b41c78d010a3280388115816c5cf5f4 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 14 Aug 2019 18:21:53 +0100 Subject: [PATCH 15/35] remove the restriction on fiber for the constraint restriction was placed on fiber type because when using excel input autodesign create fiber names with syntax not explicit to the user, so that entering a node name as constraint may end up to a wrong interpretation eg aa -bb -> create names such as 'fiber aa to bb', fiber bb to aa' . If user constraint is bb there is a ambiguity on the fiber direction. However this is not a problem for user using json format. So I preferred to removed this constraint now. a later PR solves this naming ambiguity Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 108c0b7f0..da1736cc7 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -214,7 +214,7 @@ def correct_route_list(network, pathreqlist): # prepares the format of route list of nodes to be consistant # remove wrong names, remove endpoints # also correct source and destination - anytype = [n.uid for n in network.nodes() if not isinstance(n, Transceiver) and not isinstance(n, Fiber)] + anytype = [n.uid for n in network.nodes() if not isinstance(n, Transceiver)] # TODO there is a problem of identification of fibers in case of parallel fibers bitween two adjacent roadms # so fiber constraint is not supported transponders = [n.uid for n in network.nodes() if isinstance(n, Transceiver)] From 149a0da8c98486b9de7859e3491b57034eb593d8 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 14 Aug 2019 18:33:31 +0100 Subject: [PATCH 16/35] update csv creation according to all model changes Signed-off-by: EstherLerouzic --- gnpy/core/request.py | 129 +++++++++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 54 deletions(-) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 5d70d0f8f..610fe61fd 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -395,65 +395,86 @@ def jsontocsv(json_data,equipment,fileout): # and write results in an CSV file mywriter = writer(fileout) - mywriter.writerow(('path-id','source','destination','path_bandwidth','Pass?',\ + mywriter.writerow(('response-id','source','destination','path_bandwidth','Pass?',\ 'nb of tsp pairs','total cost','transponder-type','transponder-mode',\ 'OSNR@0.1nm','SNR@0.1nm','SNR@bandwidth','baud rate (Gbaud)',\ 'input power (dBm)','path')) 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'] - # selects only roadm nodes - pth = ' | '.join([ e['path-route-object']['unnumbered-hop']['node-id'] - for e in p['path-properties']['path-route-objects'] - if e['path-route-object']['unnumbered-hop']['node-id'].startswith('roadm') or e['path-route-object']['unnumbered-hop']['node-id'].startswith('Edfa')]) - - [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) - # loading equipment already tests the existence of tsp type and mode: - if mode !='not feasible with this transponder' : - [minosnr, baud_rate, bit_rate, cost] = next([m['OSNR'] , m['baud_rate'] , m['bit_rate'], m['cost']] - for m in equipment['Transceiver'][tsp].mode if m['format']==mode) - # else: - # [minosnr, baud_rate, bit_rate] = ['','','',''] - 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') - path_bandwidth = next(e['accumulative-value'] - for e in p['path-properties']['path-metric'] if e['metric-type'] == 'path_bandwidth') - if isinstance(output_snr, str): - isok = False - nb_tsp = 0 - pthbdbw = round(path_bandwidth*1e-9,2) - rosnr = '' - rsnr = '' - rsnrb = '' - br = '' - pw = '' - total_cost = '' - else: - isok = output_snr >= minosnr - nb_tsp = ceil(path_bandwidth / bit_rate) - pthbdbw = round(path_bandwidth*1e-9,2) - rosnr = round(output_osnr,2) - rsnr = round(output_snr,2) - rsnrb = round(output_snrbandwidth,2) - br = round(baud_rate*1e-9,2) - pw = round(lin2db(power)+30,2) - total_cost = nb_tsp * cost + + for p in json_data['response']: + path_id = p['response-id'] + try: + if p['no-path'] : + isok = False + nb_tsp = 0 + pthbdbw = round(path_bandwidth*1e-9,2) + rosnr = '' + rsnr = '' + rsnrb = '' + br = '' + pw = '' + total_cost = '' + pth = '' + except KeyError: + source = p['path-properties']['path-route-objects'][0]\ + ['path-route-object']['num-unnum-hop']['node-id'] + destination = p['path-properties']['path-route-objects'][-2]\ + ['path-route-object']['num-unnum-hop']['node-id'] + # selects only roadm nodes + temp = [] + for e in p['path-properties']['path-route-objects'] : + try : + temp .append(e['path-route-object']['num-unnum-hop']['node-id']) + except KeyError: + pass + pth = ' | '.join(temp) + + temp_tsp = pth_el['path-properties']['path-route-objects'][1]\ + ['path-route-object']['transponder'] + tsp = temp_tsp['transponder-type'] + mode = temp_tsp['transponder-mode'] + + # find the min acceptable OSNR, baud rate from the eqpt library based on tsp (tupe) and mode (format) + # loading equipment already tests the existence of tsp type and mode: + if mode !='not feasible with this transponder' : + [minosnr, baud_rate, bit_rate, cost] = next([m['OSNR'] , m['baud_rate'] , m['bit_rate'], m['cost']] + for m in equipment['Transceiver'][tsp].mode if m['format']==mode) + # else: + # [minosnr, baud_rate, bit_rate] = ['','','',''] + 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') + path_bandwidth = next(e['accumulative-value'] + for e in p['path-properties']['path-metric'] if e['metric-type'] == 'path_bandwidth') + if isinstance(output_snr, str): + isok = False + nb_tsp = 0 + pthbdbw = round(path_bandwidth*1e-9,2) + rosnr = '' + rsnr = '' + rsnrb = '' + br = '' + pw = '' + total_cost = '' + else: + isok = output_snr >= minosnr + nb_tsp = ceil(path_bandwidth / bit_rate) + pthbdbw = round(path_bandwidth*1e-9,2) + rosnr = round(output_osnr,2) + rsnr = round(output_snr,2) + rsnrb = round(output_snrbandwidth,2) + br = round(baud_rate*1e-9,2) + pw = round(lin2db(power)+30,2) + total_cost = nb_tsp * cost + mywriter.writerow((path_id, source, destination, From 7849782173ab4f70c195c27f6cce0fcad8d7261e Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 19 Aug 2019 12:24:33 +0100 Subject: [PATCH 17/35] Change content of source and destination to transponder end-points previously contained the user-given source name in excel file, but could differ from effective transponder source/destination now both source and src-tp-id contain the same info (gnpy does not make difference between node and ports) Signed-off-by: EstherLerouzic --- examples/meshTopologyExampleV2_services.json | 32 ++++---- examples/path_requests_run.py | 4 +- gnpy/core/service_sheet.py | 4 +- .../data/testTopology_services_expected.json | 23 +++--- tests/data/testTopology_testservices.json | 76 +++++++++---------- 5 files changed, 68 insertions(+), 71 deletions(-) diff --git a/examples/meshTopologyExampleV2_services.json b/examples/meshTopologyExampleV2_services.json index 91e4d2a84..77487883b 100644 --- a/examples/meshTopologyExampleV2_services.json +++ b/examples/meshTopologyExampleV2_services.json @@ -2,8 +2,8 @@ "path-request": [ { "request-id": "0", - "source": "Lorient_KMA", - "destination": "Vannes_KBE", + "source": "trx Lorient_KMA", + "destination": "trx Vannes_KBE", "src-tp-id": "trx Lorient_KMA", "dst-tp-id": "trx Vannes_KBE", "path-constraints": { @@ -26,8 +26,8 @@ }, { "request-id": "1", - "source": "Brest_KLA", - "destination": "Vannes_KBE", + "source": "trx Brest_KLA", + "destination": "trx Vannes_KBE", "src-tp-id": "trx Brest_KLA", "dst-tp-id": "trx Vannes_KBE", "path-constraints": { @@ -90,8 +90,8 @@ }, { "request-id": "3", - "source": "Lannion_CAS", - "destination": "Rennes_STA", + "source": "trx Lannion_CAS", + "destination": "trx Rennes_STA", "src-tp-id": "trx Lannion_CAS", "dst-tp-id": "trx Rennes_STA", "path-constraints": { @@ -114,8 +114,8 @@ }, { "request-id": "4", - "source": "Rennes_STA", - "destination": "Lannion_CAS", + "source": "trx Rennes_STA", + "destination": "trx Lannion_CAS", "src-tp-id": "trx Rennes_STA", "dst-tp-id": "trx Lannion_CAS", "path-constraints": { @@ -138,8 +138,8 @@ }, { "request-id": "5", - "source": "Rennes_STA", - "destination": "Lannion_CAS", + "source": "trx Rennes_STA", + "destination": "trx Lannion_CAS", "src-tp-id": "trx Rennes_STA", "dst-tp-id": "trx Lannion_CAS", "path-constraints": { @@ -162,8 +162,8 @@ }, { "request-id": "6", - "source": "Lannion_CAS", - "destination": "Lorient_KMA", + "source": "trx Lannion_CAS", + "destination": "trx Lorient_KMA", "src-tp-id": "trx Lannion_CAS", "dst-tp-id": "trx Lorient_KMA", "path-constraints": { @@ -189,8 +189,8 @@ }, { "request-id": "7", - "source": "Lannion_CAS", - "destination": "Lorient_KMA", + "source": "trx Lannion_CAS", + "destination": "trx Lorient_KMA", "src-tp-id": "trx Lannion_CAS", "dst-tp-id": "trx Lorient_KMA", "path-constraints": { @@ -216,8 +216,8 @@ }, { "request-id": "7b", - "source": "Lannion_CAS", - "destination": "Lorient_KMA", + "source": "trx Lannion_CAS", + "destination": "trx Lorient_KMA", "src-tp-id": "trx Lannion_CAS", "dst-tp-id": "trx Lorient_KMA", "path-constraints": { diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index da1736cc7..d0371bea0 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -55,8 +55,8 @@ def requests_from_json(json_data,equipment): # init all params from request params = {} params['request_id'] = req['request-id'] - params['source'] = req['src-tp-id'] - params['destination'] = req['dst-tp-id'] + params['source'] = req['source'] + params['destination'] = req['destination'] params['trx_type'] = req['path-constraints']['te-bandwidth']['trx_type'] params['trx_mode'] = req['path-constraints']['te-bandwidth']['trx_mode'] params['format'] = params['trx_mode'] diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index 5854b01c3..72c42c79c 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -48,8 +48,8 @@ def __init__(self,Request,eqpt_filename): # excel has automatic number formatting that adds .0 on integer values # the next lines recover the pure int value, assuming this .0 is unwanted self.request_id = correct_xlrd_int_to_str_reading(Request.request_id) - self.source = Request.source - self.destination = Request.destination + self.source = f'trx {Request.source}' + self.destination = f'trx {Request.destination}' # TODO: the automatic naming generated by excel parser requires that source and dest name # be a string starting with 'trx' : this is manually added here. self.srctpid = f'trx {Request.source}' diff --git a/tests/data/testTopology_services_expected.json b/tests/data/testTopology_services_expected.json index c36aad296..2893e2af0 100644 --- a/tests/data/testTopology_services_expected.json +++ b/tests/data/testTopology_services_expected.json @@ -2,8 +2,8 @@ "path-request": [ { "request-id": "0", - "source": "Lorient_KMA", - "destination": "Vannes_KBE", + "source": "trx Lorient_KMA", + "destination": "trx Vannes_KBE", "src-tp-id": "trx Lorient_KMA", "dst-tp-id": "trx Vannes_KBE", "path-constraints": { @@ -29,8 +29,8 @@ }, { "request-id": "1", - "source": "Brest_KLA", - "destination": "Vannes_KBE", + "source": "trx Brest_KLA", + "destination": "trx Vannes_KBE", "src-tp-id": "trx Brest_KLA", "dst-tp-id": "trx Vannes_KBE", "path-constraints": { @@ -117,8 +117,8 @@ }, { "request-id": "3", - "source": "Lannion_CAS", - "destination": "Rennes_STA", + "source": "trx Lannion_CAS", + "destination": "trx Rennes_STA", "src-tp-id": "trx Lannion_CAS", "dst-tp-id": "trx Rennes_STA", "path-constraints": { @@ -144,8 +144,8 @@ }, { "request-id": "4", - "source": "Rennes_STA", - "destination": "Lannion_CAS", + "source": "trx Rennes_STA", + "destination": "trx Lannion_CAS", "src-tp-id": "trx Rennes_STA", "dst-tp-id": "trx Lannion_CAS", "path-constraints": { @@ -164,15 +164,12 @@ "output-power": null, "path_bandwidth": 150000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { "request-id": "5", - "source": "Rennes_STA", - "destination": "Lannion_CAS", + "source": "trx Rennes_STA", + "destination": "trx Lannion_CAS", "src-tp-id": "trx Rennes_STA", "dst-tp-id": "trx Lannion_CAS", "path-constraints": { diff --git a/tests/data/testTopology_testservices.json b/tests/data/testTopology_testservices.json index 83e5ae85b..97f7bc908 100644 --- a/tests/data/testTopology_testservices.json +++ b/tests/data/testTopology_testservices.json @@ -2,8 +2,8 @@ "path-request": [ { "request-id": "1", - "source": "a", - "destination": "g", + "source": "trx a", + "destination": "trx g", "src-tp-id": "trx a", "dst-tp-id": "trx g", "path-constraints": { @@ -29,8 +29,8 @@ }, { "request-id": "2a", - "source": "a", - "destination": "h", + "source": "trx a", + "destination": "trx h", "src-tp-id": "trx a", "dst-tp-id": "trx h", "path-constraints": { @@ -56,8 +56,8 @@ }, { "request-id": "3", - "source": "f", - "destination": "b", + "source": "trx f", + "destination": "trx b", "src-tp-id": "trx f", "dst-tp-id": "trx b", "path-constraints": { @@ -83,8 +83,8 @@ }, { "request-id": "ee", - "source": "c", - "destination": "f", + "source": "trx c", + "destination": "trx f", "src-tp-id": "trx c", "dst-tp-id": "trx f", "path-constraints": { @@ -141,8 +141,8 @@ }, { "request-id": "ff", - "source": "c", - "destination": "f", + "source": "trx c", + "destination": "trx f", "src-tp-id": "trx c", "dst-tp-id": "trx f", "path-constraints": { @@ -168,8 +168,8 @@ }, { "request-id": "10", - "source": "a", - "destination": "g", + "source": "trx a", + "destination": "trx g", "src-tp-id": "trx a", "dst-tp-id": "trx g", "path-constraints": { @@ -195,8 +195,8 @@ }, { "request-id": "11", - "source": "a", - "destination": "h", + "source": "trx a", + "destination": "trx h", "src-tp-id": "trx a", "dst-tp-id": "trx h", "path-constraints": { @@ -238,8 +238,8 @@ }, { "request-id": "12", - "source": "f", - "destination": "b", + "source": "trx f", + "destination": "trx b", "src-tp-id": "trx f", "dst-tp-id": "trx b", "path-constraints": { @@ -281,8 +281,8 @@ }, { "request-id": "13", - "source": "c", - "destination": "f", + "source": "trx c", + "destination": "trx f", "src-tp-id": "trx c", "dst-tp-id": "trx f", "path-constraints": { @@ -308,8 +308,8 @@ }, { "request-id": "14", - "source": "c", - "destination": "f", + "source": "trx c", + "destination": "trx f", "src-tp-id": "trx c", "dst-tp-id": "trx f", "path-constraints": { @@ -366,8 +366,8 @@ }, { "request-id": "e:1# /", - "source": "a", - "destination": "g", + "source": "trx a", + "destination": "trx g", "src-tp-id": "trx a", "dst-tp-id": "trx g", "path-constraints": { @@ -393,8 +393,8 @@ }, { "request-id": "b-2a", - "source": "a", - "destination": "h", + "source": "trx a", + "destination": "trx h", "src-tp-id": "trx a", "dst-tp-id": "trx h", "path-constraints": { @@ -420,8 +420,8 @@ }, { "request-id": "3a;?", - "source": "f", - "destination": "b", + "source": "trx f", + "destination": "trx b", "src-tp-id": "trx f", "dst-tp-id": "trx b", "path-constraints": { @@ -447,8 +447,8 @@ }, { "request-id": "ee-s", - "source": "c", - "destination": "f", + "source": "trx c", + "destination": "trx f", "src-tp-id": "trx c", "dst-tp-id": "trx f", "path-constraints": { @@ -505,8 +505,8 @@ }, { "request-id": "ff-b", - "source": "c", - "destination": "f", + "source": "trx c", + "destination": "trx f", "src-tp-id": "trx c", "dst-tp-id": "trx f", "path-constraints": { @@ -532,8 +532,8 @@ }, { "request-id": "10-z", - "source": "a", - "destination": "g", + "source": "trx a", + "destination": "trx g", "src-tp-id": "trx a", "dst-tp-id": "trx g", "path-constraints": { @@ -559,8 +559,8 @@ }, { "request-id": "11 g", - "source": "a", - "destination": "h", + "source": "trx a", + "destination": "trx h", "src-tp-id": "trx a", "dst-tp-id": "trx h", "path-constraints": { @@ -586,8 +586,8 @@ }, { "request-id": "12<", - "source": "f", - "destination": "b", + "source": "trx f", + "destination": "trx b", "src-tp-id": "trx f", "dst-tp-id": "trx b", "path-constraints": { @@ -613,8 +613,8 @@ }, { "request-id": "12>", - "source": "f", - "destination": "b", + "source": "trx f", + "destination": "trx b", "src-tp-id": "trx f", "dst-tp-id": "trx b", "path-constraints": { From e8e126a6ce73611c2031342daa906fd29a82c258 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 19 Aug 2019 12:35:07 +0100 Subject: [PATCH 18/35] update existing test files and examples according to ietf yang model - 'loose' and 'strict' changed to 'LOOSE' and 'STRICT' - n, m changed to N, M - unused objects removed - ... Also correct templates for service and response Signed-off-by: EstherLerouzic --- examples/meshTopologyExampleV2_services.json | 21 +-- path_result_template.json | 86 ++++++---- service-template.json | 64 ++++--- .../data/testTopology_services_expected.json | 91 +++------- tests/data/testTopology_testservices.json | 161 ++++-------------- 5 files changed, 163 insertions(+), 260 deletions(-) diff --git a/examples/meshTopologyExampleV2_services.json b/examples/meshTopologyExampleV2_services.json index 77487883b..52dc76c25 100644 --- a/examples/meshTopologyExampleV2_services.json +++ b/examples/meshTopologyExampleV2_services.json @@ -173,8 +173,8 @@ "trx_mode": "mode 1", "effective-freq-slot": [ { - "n": "null", - "m": "null" + "N": "null", + "M": "null" } ], "spacing": 50000000000.0, @@ -182,9 +182,6 @@ "output-power": 0.001, "path_bandwidth": 300000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -200,8 +197,8 @@ "trx_mode": "mode 1", "effective-freq-slot": [ { - "n": "null", - "m": "null" + "N": "null", + "M": "null" } ], "spacing": 50000000000.0, @@ -209,9 +206,6 @@ "output-power": 0.001, "path_bandwidth": 400000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -227,8 +221,8 @@ "trx_mode": "mode 1", "effective-freq-slot": [ { - "n": "null", - "m": "null" + "N": "null", + "M": "null" } ], "spacing": 75000000000.0, @@ -236,9 +230,6 @@ "output-power": 0.001, "path_bandwidth": 400000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } } ], diff --git a/path_result_template.json b/path_result_template.json index 68a6c1b63..f357f14a1 100644 --- a/path_result_template.json +++ b/path_result_template.json @@ -1,39 +1,63 @@ { - "paths": [ + "response": [ { - "path": { - "path-id": null, - "path-properties": { - "path-metric": [ - { - "metric-type": null, - "accumulative-value": null + "response-id": null, + "path-properties": { + "path-metric": [ + { + "metric-type": "SNR@bandwidth", + "accumulative-value": null + }, + { + "metric-type": "SNR@0.1nm", + "accumulative-value": null + }, + { + "metric-type": "OSNR@bandwidth", + "accumulative-value": null + }, + { + "metric-type": "OSNR@0.1nm", + "accumulative-value": null + }, + { + "metric-type": "reference_power", + "accumulative-value": null + }, + { + "metric-type": "path_bandwidth", + "accumulative-value": null + } + ], + "path-route-objects": [ + { + "path-route-object": { + "index": 0, + "num-unnum-hop": { + "node-id": null, + "link-tp-id": null + } } - ], - "path-srlgs": { - "usage": "not used yet", - "values": ["not used yet"] - }, - "path-route-objects": [ - { - "path-route-object": { - "index": null, - "unnumbered-hop": { - "node-id": null, - "link-tp-id": null, - "hop-type": null, - "direction": "not used" - }, - "label-hop": { - "te-label": { - "generic": "not used yet", - "direction": "not used yet" - } - } + }, + { + "path-route-object": { + "index": 1, + "transponder": { + "transponder-type": null, + "transponder-mode": null } } - ] - } + }, + { + "path-route-object": { + "index": 2, + "num-unnum-hop": { + "node-id": null, + "link-tp-id": null + } + } + }, + ] } } ] diff --git a/service-template.json b/service-template.json index 8d7a1d777..fd53d2dd1 100644 --- a/service-template.json +++ b/service-template.json @@ -6,6 +6,44 @@ "destination": null, "src-tp-id": null, "dst-tp-id": null, + "explicit-route-objects": { + "route-object-include-exclude": [ + { + "explicit-route-usage": null, + "index": null, + "num-unnum-hop": { + "node-id": null, + "link-tp-id": null, + "hop-type": null + } + }, + { + "explicit-route-usage": null, + "index": null, + "label-hop": { + "N": null, + "M": null + } + }, + { + "explicit-route-usage": null, + "index": null, + "transponder": { + "transponder-type": null, + "transponder-mode": null + } + }, + { + "explicit-route-usage": null, + "index": null, + "regenerator": { + "regenerator-id": null, + "transponder-type": null, + "transponder-mode": null + } + } + ] + }, "path-constraints": { "te-bandwidth": { "technology": "flexi-grid", @@ -22,27 +60,6 @@ "output-power": null, "path_bandwidth": null } - }, - "optimizations": { - "explicit-route-include-objects": { - "route-object-include-object": [ - { - "index": null, - "unnumbered-hop": { - "node-id": null, - "link-tp-id": "link-tp-id is not used", - "hop-type": null, - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } - } - } - ] - } } }], "synchronization": [ @@ -50,10 +67,9 @@ "synchronization-id": null, "svec": { "relaxable": "True", - "link-diverse": "False", - "node-diverse": "False", + "disjointness": "node link", "request-id-number": [ - null ] + null, null ] }, } ] diff --git a/tests/data/testTopology_services_expected.json b/tests/data/testTopology_services_expected.json index 2893e2af0..48a020b49 100644 --- a/tests/data/testTopology_services_expected.json +++ b/tests/data/testTopology_services_expected.json @@ -13,8 +13,8 @@ "trx_mode": "mode 1", "effective-freq-slot": [ { - "n": "null", - "m": "null" + "N": "null", + "M": "null" } ], "spacing": 50000000000.0, @@ -22,9 +22,6 @@ "output-power": null, "path_bandwidth": 100000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -40,8 +37,8 @@ "trx_mode": "mode 1", "effective-freq-slot": [ { - "n": "null", - "m": "null" + "N": "null", + "M": "null" } ], "spacing": 50000000000.0, @@ -50,66 +47,42 @@ "path_bandwidth": 0 } }, - "optimizations": { - "explicit-route-include-objects": [ + "explicit-route-objects": { + "route-object-include-exclude": [ { + "explicit-route-usage": "route-include-ero", "index": 0, - "unnumbered-hop": { + "num-unnum-hop": { "node-id": "roadm Brest_KLA", "link-tp-id": "link-tp-id is not used", - "hop-type": "loose", - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } + "hop-type": "LOOSE" } }, { + "explicit-route-usage": "route-include-ero", "index": 1, - "unnumbered-hop": { + "num-unnum-hop": { "node-id": "roadm Lannion_CAS", "link-tp-id": "link-tp-id is not used", - "hop-type": "loose", - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } + "hop-type": "LOOSE" } }, { + "explicit-route-usage": "route-include-ero", "index": 2, - "unnumbered-hop": { + "num-unnum-hop": { "node-id": "roadm Lorient_KMA", "link-tp-id": "link-tp-id is not used", - "hop-type": "loose", - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } + "hop-type": "LOOSE" } }, { + "explicit-route-usage": "route-include-ero", "index": 3, - "unnumbered-hop": { + "num-unnum-hop": { "node-id": "roadm Vannes_KBE", "link-tp-id": "link-tp-id is not used", - "hop-type": "loose", - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } + "hop-type": "LOOSE" } } ] @@ -128,8 +101,8 @@ "trx_mode": "mode 1", "effective-freq-slot": [ { - "n": "null", - "m": "null" + "N": "null", + "M": "null" } ], "spacing": 50000000000.0, @@ -137,9 +110,6 @@ "output-power": 0.0012589254117941673, "path_bandwidth": 60000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -155,8 +125,8 @@ "trx_mode": "mode 2", "effective-freq-slot": [ { - "n": "null", - "m": "null" + "N": "null", + "M": "null" } ], "spacing": 75000000000.0, @@ -179,8 +149,8 @@ "trx_mode": "mode 2", "effective-freq-slot": [ { - "n": "null", - "m": "null" + "N": "null", + "M": "null" } ], "spacing": 75000000000.0, @@ -188,9 +158,6 @@ "output-power": 0.0019952623149688794, "path_bandwidth": 20000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } } ], @@ -198,9 +165,8 @@ { "synchronization-id": "3", "svec": { - "relaxable": "False", - "link-diverse": "True", - "node-diverse": "True", + "relaxable": "false", + "disjointness": "node link", "request-id-number": [ "3", "1" @@ -210,9 +176,8 @@ { "synchronization-id": "4", "svec": { - "relaxable": "False", - "link-diverse": "True", - "node-diverse": "True", + "relaxable": "false", + "disjointness": "node link", "request-id-number": [ "4", "5" diff --git a/tests/data/testTopology_testservices.json b/tests/data/testTopology_testservices.json index 97f7bc908..1ea6b45e8 100644 --- a/tests/data/testTopology_testservices.json +++ b/tests/data/testTopology_testservices.json @@ -22,9 +22,6 @@ "output-power": 0.001, "path_bandwidth": 300000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -49,9 +46,6 @@ "output-power": 0.001, "path_bandwidth": 300000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -76,9 +70,6 @@ "output-power": 0.001, "path_bandwidth": 300000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -104,36 +95,23 @@ "path_bandwidth": 300000000000.0 } }, - "optimizations": { - "explicit-route-include-objects": [ + "explicit-route-objects": { + "route-object-include-exclude": [ { + "explicit-route-usage": "route-include-ero", "index": 0, - "unnumbered-hop": { + "num-unnum-hop": { "node-id": "roadm e", - "link-tp-id": "link-tp-id is not used", - "hop-type": "loose", - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } + "hop-type": "LOOSE" } }, { + "explicit-route-usage": "route-include-ero", "index": 1, - "unnumbered-hop": { + "num-unnum-hop": { "node-id": "roadm g", "link-tp-id": "link-tp-id is not used", - "hop-type": "loose", - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } + "hop-type": "LOOSE" } } ] @@ -161,9 +139,6 @@ "output-power": 0.001, "path_bandwidth": 300000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -188,9 +163,6 @@ "output-power": 0.001, "path_bandwidth": 300000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -216,21 +188,15 @@ "path_bandwidth": 300000000000.0 } }, - "optimizations": { - "explicit-route-include-objects": [ + "explicit-route-objects": { + "route-object-include-exclude": [ { + "explicit-route-usage": "route-include-ero", "index": 0, - "unnumbered-hop": { + "num-unnum-hop": { "node-id": "bb", "link-tp-id": "link-tp-id is not used", - "hop-type": "loose", - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } + "hop-type": "LOOSE" } } ] @@ -259,21 +225,15 @@ "path_bandwidth": 300000000000.0 } }, - "optimizations": { - "explicit-route-include-objects": [ + "explicit-route-objects": { + "route-object-include-exclude": [ { + "explicit-route-usage": "route-include-ero", "index": 0, - "unnumbered-hop": { + "num-unnum-hop": { "node-id": "trx b", "link-tp-id": "link-tp-id is not used", - "hop-type": "loose", - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } + "hop-type": "LOOSE" } } ] @@ -301,9 +261,6 @@ "output-power": 0.001, "path_bandwidth": 300000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -329,36 +286,23 @@ "path_bandwidth": 300000000000.0 } }, - "optimizations": { - "explicit-route-include-objects": [ + "explicit-route-objects": { + "route-object-include-exclude": [ { + "explicit-route-usage": "route-include-ero", "index": 0, - "unnumbered-hop": { + "num-unnum-hop": { "node-id": "roadm e", - "link-tp-id": "link-tp-id is not used", - "hop-type": "loose", - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } + "hop-type": "LOOSE" } }, { + "explicit-route-usage": "route-include-ero", "index": 1, - "unnumbered-hop": { + "num-unnum-hop": { "node-id": "roadm g", "link-tp-id": "link-tp-id is not used", - "hop-type": "loose", - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } + "hop-type": "LOOSE" } } ] @@ -386,9 +330,6 @@ "output-power": null, "path_bandwidth": 300000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -413,9 +354,6 @@ "output-power": 0.001, "path_bandwidth": 300000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -440,9 +378,6 @@ "output-power": null, "path_bandwidth": 300000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -468,36 +403,23 @@ "path_bandwidth": 300000000000.0 } }, - "optimizations": { - "explicit-route-include-objects": [ + "explicit-route-objects": { + "route-object-include-exclude": [ { + "explicit-route-usage": "route-include-ero", "index": 0, - "unnumbered-hop": { + "num-unnum-hop": { "node-id": "roadm e", - "link-tp-id": "link-tp-id is not used", - "hop-type": "loose", - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } + "hop-type": "LOOSE" } }, { + "explicit-route-usage": "route-include-ero", "index": 1, - "unnumbered-hop": { + "num-unnum-hop": { "node-id": "roadm g", "link-tp-id": "link-tp-id is not used", - "hop-type": "loose", - "direction": "direction is not used" - }, - "label-hop": { - "te-label": { - "generic": "generic is not used", - "direction": "direction is not used" - } + "hop-type": "LOOSE" } } ] @@ -525,9 +447,6 @@ "output-power": 0.001, "path_bandwidth": 300000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -552,9 +471,6 @@ "output-power": null, "path_bandwidth": 300000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -579,9 +495,6 @@ "output-power": null, "path_bandwidth": 300000000000.0 } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -606,9 +519,6 @@ "output-power": null, "path_bandwidth": null } - }, - "optimizations": { - "explicit-route-include-objects": [] } }, { @@ -633,9 +543,6 @@ "output-power": null, "path_bandwidth": null } - }, - "optimizations": { - "explicit-route-include-objects": [] } } ], From 9ca72d6105b2d663838d77e685af98ca85a663bf Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 19 Aug 2019 12:37:22 +0100 Subject: [PATCH 19/35] Correct csv creation in case of no path previously reported the requested bandwidth, now fill it with an empty string, same as the other fields. This will be updated in a later PR when different kind of blocking will be supported Signed-off-by: EstherLerouzic --- gnpy/core/request.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 610fe61fd..fad3b06d8 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -406,9 +406,13 @@ def jsontocsv(json_data,equipment,fileout): path_id = p['response-id'] try: if p['no-path'] : + source = '' + destination = '' + tsp = '' + mode = '' isok = False nb_tsp = 0 - pthbdbw = round(path_bandwidth*1e-9,2) + pthbdbw = '' rosnr = '' rsnr = '' rsnrb = '' From 1957beb1b68d23a3d5fa07e4225842035facdf6e Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 19 Aug 2019 12:39:41 +0100 Subject: [PATCH 20/35] Remove the filtering on transponders for correcting explicit route anytype supported including transponder in order to accept well formed route list from user (if user enters 'trx Lannion_CAS' this should be accepted even if it repeats the source name) A later PR better handles route list names Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index d0371bea0..79b90cdd4 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -214,7 +214,7 @@ def correct_route_list(network, pathreqlist): # prepares the format of route list of nodes to be consistant # remove wrong names, remove endpoints # also correct source and destination - anytype = [n.uid for n in network.nodes() if not isinstance(n, Transceiver)] + anytype = [n.uid for n in network.nodes()] # TODO there is a problem of identification of fibers in case of parallel fibers bitween two adjacent roadms # so fiber constraint is not supported transponders = [n.uid for n in network.nodes() if isinstance(n, Transceiver)] From 022f743db1ec781b5ae2a233ffa68104c278df74 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 16 Aug 2019 10:33:33 +0100 Subject: [PATCH 21/35] Change the exception handling if sync vector is absent previous try section encompass errors that should not be silently ignored. Correction pointed a default in a test file. Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 8 ++++++-- tests/data/testTopology_testservices.json | 12 ++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 79b90cdd4..cc34b6b61 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -129,6 +129,11 @@ def consistency_check(params, f_max_from_si): def disjunctions_from_json(json_data): disjunctions_list = [] try: + temp_test = json_data['synchronization'] + except KeyError: + temp_test = [] + pass + if temp_test: for snc in json_data['synchronization']: params = {} params['disjunction_id'] = snc['synchronization-id'] @@ -138,8 +143,7 @@ def disjunctions_from_json(json_data): params['disjunctions_req'] = snc['svec']['request-id-number'] disjunctions_list.append(Disjunction(**params)) print(disjunctions_list) - except KeyError: - pass + return disjunctions_list diff --git a/tests/data/testTopology_testservices.json b/tests/data/testTopology_testservices.json index 1ea6b45e8..2eb6b9054 100644 --- a/tests/data/testTopology_testservices.json +++ b/tests/data/testTopology_testservices.json @@ -551,8 +551,7 @@ "synchronization-id": "1", "svec": { "relaxable": "False", - "link-diverse": "True", - "node-diverse": "True", + "disjointness": "node link", "request-id-number": [ "1", "2a" @@ -563,8 +562,7 @@ "synchronization-id": "3", "svec": { "relaxable": "False", - "link-diverse": "True", - "node-diverse": "True", + "disjointness": "node link", "request-id-number": [ "3", "1" @@ -575,8 +573,7 @@ "synchronization-id": "ff", "svec": { "relaxable": "False", - "link-diverse": "True", - "node-diverse": "True", + "disjointness": "node link", "request-id-number": [ "ff", "13" @@ -587,8 +584,7 @@ "synchronization-id": "13", "svec": { "relaxable": "False", - "link-diverse": "True", - "node-diverse": "True", + "disjointness": "node link", "request-id-number": [ "13", "14" From 609cd94798ca99f93876552a45189a68ef797ee2 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 31 May 2019 16:14:12 +0100 Subject: [PATCH 22/35] Remove @ in path-metric names and extend type to decimal64 @ character not correctly read with OpenDayLight yang tool used for transportPCE project. https://docs.opendaylight.org/en/stable-nitrogen/developer-guide/yang-tools.html#working-with-yang-model. Changed the names of path metrics from osnr@ to osnr- Signed-off-by: EstherLerouzic --- gnpy/core/request.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index fad3b06d8..929b8623e 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -157,19 +157,19 @@ def pathresult(self): 'path-properties':{ 'path-metric': [ { - 'metric-type': 'SNR@bandwidth', + 'metric-type': 'SNR-bandwidth', 'accumulative-value': round(mean(self.computed_path[-1].snr),2) }, { - 'metric-type': 'SNR@0.1nm', + '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', + 'metric-type': 'OSNR-bandwidth', 'accumulative-value': round(mean(self.computed_path[-1].osnr_ase),2) }, { - 'metric-type': 'OSNR@0.1nm', + 'metric-type': 'OSNR-0.1nm', 'accumulative-value': round(mean(self.computed_path[-1].osnr_ase_01nm),2) }, { @@ -397,7 +397,7 @@ def jsontocsv(json_data,equipment,fileout): mywriter = writer(fileout) mywriter.writerow(('response-id','source','destination','path_bandwidth','Pass?',\ 'nb of tsp pairs','total cost','transponder-type','transponder-mode',\ - 'OSNR@0.1nm','SNR@0.1nm','SNR@bandwidth','baud rate (Gbaud)',\ + 'OSNR-0.1nm','SNR-0.1nm','SNR-bandwidth','baud rate (Gbaud)',\ 'input power (dBm)','path')) tspjsondata = equipment['Transceiver'] #print(tspjsondata) @@ -446,14 +446,15 @@ def jsontocsv(json_data,equipment,fileout): for m in equipment['Transceiver'][tsp].mode if m['format']==mode) # else: # [minosnr, baud_rate, bit_rate] = ['','','',''] + output_snr = next(e['accumulative-value'] - for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@0.1nm') + 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') + 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') + 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') + 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') path_bandwidth = next(e['accumulative-value'] From dfa0a26a2897fefcd77872c62c3d266bf0c156f3 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 31 May 2019 17:16:51 +0100 Subject: [PATCH 23/35] changes to improve quality minor name refactor indent corrections minor fixes for spacing Signed-off-by: EstherLerouzic --- gnpy/core/request.py | 135 +++++++++++++++++++------------------ gnpy/core/service_sheet.py | 2 +- path_result_template.json | 2 +- 3 files changed, 70 insertions(+), 69 deletions(-) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 929b8623e..de579f78c 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -81,9 +81,9 @@ def __repr__(self): f'baud_rate:\t{temp} Gbaud', f'bit_rate:\t{temp2} Gb/s', f'spacing:\t{self.spacing * 1e-9} GHz', - f'power: \t{round(lin2db(self.power)+30,2)} dBm', + f'power: \t{round(lin2db(self.power)+30, 2)} dBm', f'nb channels: \t{self.nb_channel}', - f'path_bandwidth: \t{round(self.path_bandwidth * 1e-9,2)} Gbit/s', + f'path_bandwidth: \t{round(self.path_bandwidth * 1e-9, 2)} Gbit/s', f'nodes-list:\t{self.nodes_list}', f'loose-list:\t{self.loose_list}' '\n']) @@ -97,16 +97,16 @@ def __init__(self, *args, **params): self.disjunctions_req = params.disjunctions_req def __str__(self): - return '\n\t'.join([f'relaxable: {self.relaxable}', - f'link-diverse: {self.link_diverse}', + return '\n\t'.join([f'relaxable: {self.relaxable}', + f'link-diverse: {self.link_diverse}', f'node-diverse: {self.node_diverse}', f'request-id-numbers: {self.disjunctions_req}'] ) def __repr__(self): return '\n\t'.join([ f'{type(self).__name__} {self.disjunction_id}', f'relaxable: {self.relaxable}', - f'link-diverse: {self.link_diverse}', - f'node-diverse: {self.node_diverse}', + f'link-diverse: {self.link_diverse}', + f'node-diverse: {self.node_diverse}', f'request-id-numbers: {self.disjunctions_req}' '\n']) @@ -126,7 +126,7 @@ def pathresult(self): else: index = 0 pro_list = [] - for n in self.computed_path : + for n in self.computed_path: temp = { 'path-route-object': { 'index': index, @@ -139,7 +139,7 @@ def pathresult(self): } pro_list.append(temp) index += 1 - if isinstance(n, Transceiver) : + if isinstance(n, Transceiver): temp = { 'path-route-object': { 'index': index, @@ -153,35 +153,36 @@ def pathresult(self): index += 1 response = { - 'response-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 - }, - { - 'metric-type': 'path_bandwidth', - 'accumulative-value': self.path_request.path_bandwidth - } + 'response-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 + }, + { + 'metric-type': 'path_bandwidth', + 'accumulative-value': self.path_request.path_bandwidth + } ], - 'path-route-objects': pro_list + 'path-route-objects': pro_list } } return response @@ -231,7 +232,7 @@ def compute_constrained_path(network, req): msg = f'\x1b[1;33;40m'+f'Request {req.request_id} could not find a path from {source.uid} to node : {destination.uid} in network topology'+ '\x1b[0m' logger.critical(msg) print(msg) - total_path = [] + total_path = [] else : all_simp_pths = list(all_simple_paths(network,source=source,\ target=destination, cutoff=120)) @@ -345,7 +346,7 @@ def propagate_and_optimize_mode(path, req, equipment): # if mode is unknown : loops on the modes starting from the highest baudrate fiting in the # step 1: create an ordered list of modes based on baudrate baudrate_to_explore = list(set([m['baud_rate'] for m in equipment['Transceiver'][req.tsp].mode - if float(m['min_spacing'])<= req.spacing])) + if float(m['min_spacing'])<= req.spacing])) # TODO be carefull on limits cases if spacing very close to req spacing eg 50.001 50.000 baudrate_to_explore = sorted(baudrate_to_explore, reverse=True) if baudrate_to_explore : @@ -402,10 +403,10 @@ def jsontocsv(json_data,equipment,fileout): tspjsondata = equipment['Transceiver'] #print(tspjsondata) - for p in json_data['response']: - path_id = p['response-id'] + for pth_el in json_data['response']: + path_id = pth_el['response-id'] try: - if p['no-path'] : + if pth_el['no-path'] : source = '' destination = '' tsp = '' @@ -421,15 +422,16 @@ def jsontocsv(json_data,equipment,fileout): total_cost = '' pth = '' except KeyError: - source = p['path-properties']['path-route-objects'][0]\ + + source = pth_el['path-properties']['path-route-objects'][0]\ ['path-route-object']['num-unnum-hop']['node-id'] - destination = p['path-properties']['path-route-objects'][-2]\ + destination = pth_el['path-properties']['path-route-objects'][-2]\ ['path-route-object']['num-unnum-hop']['node-id'] # selects only roadm nodes temp = [] - for e in p['path-properties']['path-route-objects'] : + for e in pth_el['path-properties']['path-route-objects']: try : - temp .append(e['path-route-object']['num-unnum-hop']['node-id']) + temp.append(e['path-route-object']['num-unnum-hop']['node-id']) except KeyError: pass pth = ' | '.join(temp) @@ -446,19 +448,18 @@ def jsontocsv(json_data,equipment,fileout): for m in equipment['Transceiver'][tsp].mode if m['format']==mode) # else: # [minosnr, baud_rate, bit_rate] = ['','','',''] - - output_snr = next(e['accumulative-value'] - for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR-0.1nm') + output_snr = next(e['accumulative-value'] + for e in pth_el['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') + for e in pth_el['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') + for e in pth_el['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') + for e in pth_el['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') + for e in pth_el['path-properties']['path-metric'] if e['metric-type'] == 'reference_power') path_bandwidth = next(e['accumulative-value'] - for e in p['path-properties']['path-metric'] if e['metric-type'] == 'path_bandwidth') + for e in pth_el['path-properties']['path-metric'] if e['metric-type'] == 'path_bandwidth') if isinstance(output_snr, str): isok = False nb_tsp = 0 @@ -470,14 +471,14 @@ def jsontocsv(json_data,equipment,fileout): pw = '' total_cost = '' else: - isok = output_snr >= minosnr + isok = output_snr >= minosnr nb_tsp = ceil(path_bandwidth / bit_rate) pthbdbw = round(path_bandwidth*1e-9,2) - rosnr = round(output_osnr,2) - rsnr = round(output_snr,2) - rsnrb = round(output_snrbandwidth,2) - br = round(baud_rate*1e-9,2) - pw = round(lin2db(power)+30,2) + rosnr = round(output_osnr,2) + rsnr = round(output_snr,2) + rsnrb = round(output_snrbandwidth,2) + br = round(baud_rate*1e-9,2) + pw = round(lin2db(power)+30,2) total_cost = nb_tsp * cost mywriter.writerow((path_id, @@ -570,7 +571,7 @@ def __init__(self, req, pth, simplepth): all_simp_pths_reversed = [] for pth in all_simp_pths: all_simp_pths_reversed.append(find_reversed_path(pth,network)) - rqs[pathreq.request_id] = all_simp_pths + rqs[pathreq.request_id] = all_simp_pths temp =[] for p in all_simp_pths : # build a short list representing each roadm+direction with the first item @@ -843,19 +844,19 @@ def compare_reqs(req1,req2,disjlist) : req1.format == req2.format and \ req1.OSNR == req2.OSNR and \ req1.roll_off == req2.roll_off and \ - same_disj : + same_disj: return True else: return False -def requests_aggregation(pathreqlist,disjlist) : +def requests_aggregation(pathreqlist,disjlist): # this function aggregates requests so that if several requests # exist between same source and destination and with same transponder type # todo maybe add conditions on mode ??, spacing ... # currently if undefined takes the default values local_list = pathreqlist.copy() for req in pathreqlist: - for r in local_list : + for r in local_list: if req.request_id != r.request_id and compare_reqs(req, r, disjlist): # aggregate r.path_bandwidth += req.path_bandwidth @@ -865,12 +866,12 @@ def requests_aggregation(pathreqlist,disjlist) : local_list.remove(req) # todo change also disjunction req with new demand - for d in disjlist : - if req.request_id in d.disjunctions_req : + for d in disjlist: + if req.request_id in d.disjunctions_req: d.disjunctions_req.remove(req.request_id) d.disjunctions_req.append(r.request_id) - for d in disjlist : - if temp_r_id in d.disjunctions_req : + for d in disjlist: + if temp_r_id in d.disjunctions_req: disjlist.remove(d) break return local_list, disjlist diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index 72c42c79c..8e3476499 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -144,7 +144,7 @@ def pathrequest(self): 'technology': 'flexi-grid', 'trx_type' : self.trx_type, 'trx_mode' : self.mode, - 'effective-freq-slot':[{'N': 'null','M': 'null'}] , + 'effective-freq-slot':[{'N': 'null', 'M': 'null'}], 'spacing' : self.spacing, 'max-nb-of-channel' : self.nb_channel, 'output-power' : self.power diff --git a/path_result_template.json b/path_result_template.json index f357f14a1..fb20a319c 100644 --- a/path_result_template.json +++ b/path_result_template.json @@ -56,7 +56,7 @@ "link-tp-id": null } } - }, + } ] } } From c592c572d8b08b273962d6feee76b91263d81bc0 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 5 Jun 2019 16:39:20 +0100 Subject: [PATCH 24/35] adding a test for the csv creation using pandas package to have an easy ordering of response column: - test that the generated header is as expected - read the response. In order to support different orders wrt response answer and field answer, test function frst orders lines according to response index and then to columns (fields of the answer). check that the answers are as expected Signed-off-by: EstherLerouzic --- requirements.txt | 1 + tests/data/testTopology_response.json | 862 ++++++++++++++++++ tests/data/testTopology_response_expected.csv | 6 + tests/test_parser.py | 53 ++ 4 files changed, 922 insertions(+) create mode 100644 tests/data/testTopology_response.json create mode 100644 tests/data/testTopology_response_expected.csv diff --git a/requirements.txt b/requirements.txt index 3a717dee6..f6a227cc5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,7 @@ alabaster>=0.7.12,<1 matplotlib>=3.1.0,<4 networkx>=2.3,<3 numpy>=1.16.1,<2 +pandas==0.24.2 Pygments>=2.4.2,<3 pytest>=4.0.0,<5 scipy>=1.3.0,<2 diff --git a/tests/data/testTopology_response.json b/tests/data/testTopology_response.json new file mode 100644 index 000000000..d5b9948b2 --- /dev/null +++ b/tests/data/testTopology_response.json @@ -0,0 +1,862 @@ +{ + "response": [ + { + "response-id": "0", + "path-properties": { + "path-metric": [ + { + "metric-type": "SNR-bandwidth", + "accumulative-value": 26.75 + }, + { + "metric-type": "SNR-0.1nm", + "accumulative-value": 30.84 + }, + { + "metric-type": "OSNR-bandwidth", + "accumulative-value": 26.76 + }, + { + "metric-type": "OSNR-0.1nm", + "accumulative-value": 30.84 + }, + { + "metric-type": "reference_power", + "accumulative-value": 0.001 + }, + { + "metric-type": "path_bandwidth", + "accumulative-value": 100000000000.0 + } + ], + "path-route-objects": [ + { + "path-route-object": { + "index": 0, + "num-unnum-hop": { + "node-id": "trx Lorient_KMA", + "link-tp-id": "trx Lorient_KMA" + } + } + }, + { + "path-route-object": { + "index": 1, + "transponder": { + "transponder-type": "Voyager", + "transponder-mode": "mode 1" + } + } + }, + { + "path-route-object": { + "index": 2, + "num-unnum-hop": { + "node-id": "roadm Lorient_KMA", + "link-tp-id": "roadm Lorient_KMA" + } + } + }, + { + "path-route-object": { + "index": 3, + "num-unnum-hop": { + "node-id": "Edfa1_roadm Lorient_KMA", + "link-tp-id": "Edfa1_roadm Lorient_KMA" + } + } + }, + { + "path-route-object": { + "index": 4, + "num-unnum-hop": { + "node-id": "fiber (Lorient_KMA → Vannes_KBE)-F055", + "link-tp-id": "fiber (Lorient_KMA → Vannes_KBE)-F055" + } + } + }, + { + "path-route-object": { + "index": 5, + "num-unnum-hop": { + "node-id": "Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055", + "link-tp-id": "Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055" + } + } + }, + { + "path-route-object": { + "index": 6, + "num-unnum-hop": { + "node-id": "roadm Vannes_KBE", + "link-tp-id": "roadm Vannes_KBE" + } + } + }, + { + "path-route-object": { + "index": 7, + "num-unnum-hop": { + "node-id": "trx Vannes_KBE", + "link-tp-id": "trx Vannes_KBE" + } + } + }, + { + "path-route-object": { + "index": 8, + "transponder": { + "transponder-type": "Voyager", + "transponder-mode": "mode 1" + } + } + } + ] + } + }, + { + "response-id": "1", + "path-properties": { + "path-metric": [ + { + "metric-type": "SNR-bandwidth", + "accumulative-value": 18.03 + }, + { + "metric-type": "SNR-0.1nm", + "accumulative-value": 22.11 + }, + { + "metric-type": "OSNR-bandwidth", + "accumulative-value": 18.57 + }, + { + "metric-type": "OSNR-0.1nm", + "accumulative-value": 22.65 + }, + { + "metric-type": "reference_power", + "accumulative-value": 0.0012589254117941673 + }, + { + "metric-type": "path_bandwidth", + "accumulative-value": 0 + } + ], + "path-route-objects": [ + { + "path-route-object": { + "index": 0, + "num-unnum-hop": { + "node-id": "trx Brest_KLA", + "link-tp-id": "trx Brest_KLA" + } + } + }, + { + "path-route-object": { + "index": 1, + "transponder": { + "transponder-type": "Voyager", + "transponder-mode": "mode 1" + } + } + }, + { + "path-route-object": { + "index": 2, + "num-unnum-hop": { + "node-id": "roadm Brest_KLA", + "link-tp-id": "roadm Brest_KLA" + } + } + }, + { + "path-route-object": { + "index": 3, + "num-unnum-hop": { + "node-id": "Edfa0_roadm Brest_KLA", + "link-tp-id": "Edfa0_roadm Brest_KLA" + } + } + }, + { + "path-route-object": { + "index": 4, + "num-unnum-hop": { + "node-id": "fiber (Brest_KLA → Morlaix)-F060", + "link-tp-id": "fiber (Brest_KLA → Morlaix)-F060" + } + } + }, + { + "path-route-object": { + "index": 5, + "num-unnum-hop": { + "node-id": "east fused spans in Morlaix", + "link-tp-id": "east fused spans in Morlaix" + } + } + }, + { + "path-route-object": { + "index": 6, + "num-unnum-hop": { + "node-id": "fiber (Morlaix → Lannion_CAS)-F059", + "link-tp-id": "fiber (Morlaix → Lannion_CAS)-F059" + } + } + }, + { + "path-route-object": { + "index": 7, + "num-unnum-hop": { + "node-id": "west edfa in Lannion_CAS to Morlaix", + "link-tp-id": "west edfa in Lannion_CAS to Morlaix" + } + } + }, + { + "path-route-object": { + "index": 8, + "num-unnum-hop": { + "node-id": "roadm Lannion_CAS", + "link-tp-id": "roadm Lannion_CAS" + } + } + }, + { + "path-route-object": { + "index": 9, + "num-unnum-hop": { + "node-id": "east edfa in Lannion_CAS to Corlay", + "link-tp-id": "east edfa in Lannion_CAS to Corlay" + } + } + }, + { + "path-route-object": { + "index": 10, + "num-unnum-hop": { + "node-id": "fiber (Lannion_CAS → Corlay)-F061", + "link-tp-id": "fiber (Lannion_CAS → Corlay)-F061" + } + } + }, + { + "path-route-object": { + "index": 11, + "num-unnum-hop": { + "node-id": "west fused spans in Corlay", + "link-tp-id": "west fused spans in Corlay" + } + } + }, + { + "path-route-object": { + "index": 12, + "num-unnum-hop": { + "node-id": "fiber (Corlay → Loudeac)-F010", + "link-tp-id": "fiber (Corlay → Loudeac)-F010" + } + } + }, + { + "path-route-object": { + "index": 13, + "num-unnum-hop": { + "node-id": "west fused spans in Loudeac", + "link-tp-id": "west fused spans in Loudeac" + } + } + }, + { + "path-route-object": { + "index": 14, + "num-unnum-hop": { + "node-id": "fiber (Loudeac → Lorient_KMA)-F054", + "link-tp-id": "fiber (Loudeac → Lorient_KMA)-F054" + } + } + }, + { + "path-route-object": { + "index": 15, + "num-unnum-hop": { + "node-id": "Edfa0_fiber (Loudeac → Lorient_KMA)-F054", + "link-tp-id": "Edfa0_fiber (Loudeac → Lorient_KMA)-F054" + } + } + }, + { + "path-route-object": { + "index": 16, + "num-unnum-hop": { + "node-id": "roadm Lorient_KMA", + "link-tp-id": "roadm Lorient_KMA" + } + } + }, + { + "path-route-object": { + "index": 17, + "num-unnum-hop": { + "node-id": "Edfa1_roadm Lorient_KMA", + "link-tp-id": "Edfa1_roadm Lorient_KMA" + } + } + }, + { + "path-route-object": { + "index": 18, + "num-unnum-hop": { + "node-id": "fiber (Lorient_KMA → Vannes_KBE)-F055", + "link-tp-id": "fiber (Lorient_KMA → Vannes_KBE)-F055" + } + } + }, + { + "path-route-object": { + "index": 19, + "num-unnum-hop": { + "node-id": "Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055", + "link-tp-id": "Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055" + } + } + }, + { + "path-route-object": { + "index": 20, + "num-unnum-hop": { + "node-id": "roadm Vannes_KBE", + "link-tp-id": "roadm Vannes_KBE" + } + } + }, + { + "path-route-object": { + "index": 21, + "num-unnum-hop": { + "node-id": "trx Vannes_KBE", + "link-tp-id": "trx Vannes_KBE" + } + } + }, + { + "path-route-object": { + "index": 22, + "transponder": { + "transponder-type": "Voyager", + "transponder-mode": "mode 1" + } + } + } + ] + } + }, + { + "response-id": "3", + "path-properties": { + "path-metric": [ + { + "metric-type": "SNR-bandwidth", + "accumulative-value": 21.77 + }, + { + "metric-type": "SNR-0.1nm", + "accumulative-value": 25.85 + }, + { + "metric-type": "OSNR-bandwidth", + "accumulative-value": 24.2 + }, + { + "metric-type": "OSNR-0.1nm", + "accumulative-value": 28.29 + }, + { + "metric-type": "reference_power", + "accumulative-value": 0.0012589254117941673 + }, + { + "metric-type": "path_bandwidth", + "accumulative-value": 60000000000.0 + } + ], + "path-route-objects": [ + { + "path-route-object": { + "index": 0, + "num-unnum-hop": { + "node-id": "trx Lannion_CAS", + "link-tp-id": "trx Lannion_CAS" + } + } + }, + { + "path-route-object": { + "index": 1, + "transponder": { + "transponder-type": "vendorA_trx-type1", + "transponder-mode": "mode 1" + } + } + }, + { + "path-route-object": { + "index": 2, + "num-unnum-hop": { + "node-id": "roadm Lannion_CAS", + "link-tp-id": "roadm Lannion_CAS" + } + } + }, + { + "path-route-object": { + "index": 3, + "num-unnum-hop": { + "node-id": "east edfa in Lannion_CAS to Stbrieuc", + "link-tp-id": "east edfa in Lannion_CAS to Stbrieuc" + } + } + }, + { + "path-route-object": { + "index": 4, + "num-unnum-hop": { + "node-id": "fiber (Lannion_CAS → Stbrieuc)-F056", + "link-tp-id": "fiber (Lannion_CAS → Stbrieuc)-F056" + } + } + }, + { + "path-route-object": { + "index": 5, + "num-unnum-hop": { + "node-id": "east edfa in Stbrieuc to Rennes_STA", + "link-tp-id": "east edfa in Stbrieuc to Rennes_STA" + } + } + }, + { + "path-route-object": { + "index": 6, + "num-unnum-hop": { + "node-id": "fiber (Stbrieuc → Rennes_STA)-F057", + "link-tp-id": "fiber (Stbrieuc → Rennes_STA)-F057" + } + } + }, + { + "path-route-object": { + "index": 7, + "num-unnum-hop": { + "node-id": "Edfa0_fiber (Stbrieuc → Rennes_STA)-F057", + "link-tp-id": "Edfa0_fiber (Stbrieuc → Rennes_STA)-F057" + } + } + }, + { + "path-route-object": { + "index": 8, + "num-unnum-hop": { + "node-id": "roadm Rennes_STA", + "link-tp-id": "roadm Rennes_STA" + } + } + }, + { + "path-route-object": { + "index": 9, + "num-unnum-hop": { + "node-id": "trx Rennes_STA", + "link-tp-id": "trx Rennes_STA" + } + } + }, + { + "path-route-object": { + "index": 10, + "transponder": { + "transponder-type": "vendorA_trx-type1", + "transponder-mode": "mode 1" + } + } + } + ] + } + }, + { + "response-id": "4", + "path-properties": { + "path-metric": [ + { + "metric-type": "SNR-bandwidth", + "accumulative-value": 15.05 + }, + { + "metric-type": "SNR-0.1nm", + "accumulative-value": 22.15 + }, + { + "metric-type": "OSNR-bandwidth", + "accumulative-value": 15.18 + }, + { + "metric-type": "OSNR-0.1nm", + "accumulative-value": 22.27 + }, + { + "metric-type": "reference_power", + "accumulative-value": 0.001 + }, + { + "metric-type": "path_bandwidth", + "accumulative-value": 150000000000.0 + } + ], + "path-route-objects": [ + { + "path-route-object": { + "index": 0, + "num-unnum-hop": { + "node-id": "trx Rennes_STA", + "link-tp-id": "trx Rennes_STA" + } + } + }, + { + "path-route-object": { + "index": 1, + "transponder": { + "transponder-type": "vendorA_trx-type1", + "transponder-mode": "mode 2" + } + } + }, + { + "path-route-object": { + "index": 2, + "num-unnum-hop": { + "node-id": "roadm Rennes_STA", + "link-tp-id": "roadm Rennes_STA" + } + } + }, + { + "path-route-object": { + "index": 3, + "num-unnum-hop": { + "node-id": "Edfa1_roadm Rennes_STA", + "link-tp-id": "Edfa1_roadm Rennes_STA" + } + } + }, + { + "path-route-object": { + "index": 4, + "num-unnum-hop": { + "node-id": "fiber (Rennes_STA → Ploermel)-", + "link-tp-id": "fiber (Rennes_STA → Ploermel)-" + } + } + }, + { + "path-route-object": { + "index": 5, + "num-unnum-hop": { + "node-id": "east edfa in Ploermel to Vannes_KBE", + "link-tp-id": "east edfa in Ploermel to Vannes_KBE" + } + } + }, + { + "path-route-object": { + "index": 6, + "num-unnum-hop": { + "node-id": "fiber (Ploermel → Vannes_KBE)-", + "link-tp-id": "fiber (Ploermel → Vannes_KBE)-" + } + } + }, + { + "path-route-object": { + "index": 7, + "num-unnum-hop": { + "node-id": "Edfa0_fiber (Ploermel → Vannes_KBE)-", + "link-tp-id": "Edfa0_fiber (Ploermel → Vannes_KBE)-" + } + } + }, + { + "path-route-object": { + "index": 8, + "num-unnum-hop": { + "node-id": "roadm Vannes_KBE", + "link-tp-id": "roadm Vannes_KBE" + } + } + }, + { + "path-route-object": { + "index": 9, + "num-unnum-hop": { + "node-id": "Edfa0_roadm Vannes_KBE", + "link-tp-id": "Edfa0_roadm Vannes_KBE" + } + } + }, + { + "path-route-object": { + "index": 10, + "num-unnum-hop": { + "node-id": "fiber (Vannes_KBE → Lorient_KMA)-F055", + "link-tp-id": "fiber (Vannes_KBE → Lorient_KMA)-F055" + } + } + }, + { + "path-route-object": { + "index": 11, + "num-unnum-hop": { + "node-id": "Edfa0_fiber (Vannes_KBE → Lorient_KMA)-F055", + "link-tp-id": "Edfa0_fiber (Vannes_KBE → Lorient_KMA)-F055" + } + } + }, + { + "path-route-object": { + "index": 12, + "num-unnum-hop": { + "node-id": "roadm Lorient_KMA", + "link-tp-id": "roadm Lorient_KMA" + } + } + }, + { + "path-route-object": { + "index": 13, + "num-unnum-hop": { + "node-id": "Edfa0_roadm Lorient_KMA", + "link-tp-id": "Edfa0_roadm Lorient_KMA" + } + } + }, + { + "path-route-object": { + "index": 14, + "num-unnum-hop": { + "node-id": "fiber (Lorient_KMA → Loudeac)-F054", + "link-tp-id": "fiber (Lorient_KMA → Loudeac)-F054" + } + } + }, + { + "path-route-object": { + "index": 15, + "num-unnum-hop": { + "node-id": "east fused spans in Loudeac", + "link-tp-id": "east fused spans in Loudeac" + } + } + }, + { + "path-route-object": { + "index": 16, + "num-unnum-hop": { + "node-id": "fiber (Loudeac → Corlay)-F010", + "link-tp-id": "fiber (Loudeac → Corlay)-F010" + } + } + }, + { + "path-route-object": { + "index": 17, + "num-unnum-hop": { + "node-id": "east fused spans in Corlay", + "link-tp-id": "east fused spans in Corlay" + } + } + }, + { + "path-route-object": { + "index": 18, + "num-unnum-hop": { + "node-id": "fiber (Corlay → Lannion_CAS)-F061", + "link-tp-id": "fiber (Corlay → Lannion_CAS)-F061" + } + } + }, + { + "path-route-object": { + "index": 19, + "num-unnum-hop": { + "node-id": "west edfa in Lannion_CAS to Corlay", + "link-tp-id": "west edfa in Lannion_CAS to Corlay" + } + } + }, + { + "path-route-object": { + "index": 20, + "num-unnum-hop": { + "node-id": "roadm Lannion_CAS", + "link-tp-id": "roadm Lannion_CAS" + } + } + }, + { + "path-route-object": { + "index": 21, + "num-unnum-hop": { + "node-id": "trx Lannion_CAS", + "link-tp-id": "trx Lannion_CAS" + } + } + }, + { + "path-route-object": { + "index": 22, + "transponder": { + "transponder-type": "vendorA_trx-type1", + "transponder-mode": "mode 2" + } + } + } + ] + } + }, + { + "response-id": "5", + "path-properties": { + "path-metric": [ + { + "metric-type": "SNR-bandwidth", + "accumulative-value": 21.68 + }, + { + "metric-type": "SNR-0.1nm", + "accumulative-value": 28.77 + }, + { + "metric-type": "OSNR-bandwidth", + "accumulative-value": 23.7 + }, + { + "metric-type": "OSNR-0.1nm", + "accumulative-value": 30.79 + }, + { + "metric-type": "reference_power", + "accumulative-value": 0.0019952623149688794 + }, + { + "metric-type": "path_bandwidth", + "accumulative-value": 20000000000.0 + } + ], + "path-route-objects": [ + { + "path-route-object": { + "index": 0, + "num-unnum-hop": { + "node-id": "trx Rennes_STA", + "link-tp-id": "trx Rennes_STA" + } + } + }, + { + "path-route-object": { + "index": 1, + "transponder": { + "transponder-type": "vendorA_trx-type1", + "transponder-mode": "mode 2" + } + } + }, + { + "path-route-object": { + "index": 2, + "num-unnum-hop": { + "node-id": "roadm Rennes_STA", + "link-tp-id": "roadm Rennes_STA" + } + } + }, + { + "path-route-object": { + "index": 3, + "num-unnum-hop": { + "node-id": "Edfa0_roadm Rennes_STA", + "link-tp-id": "Edfa0_roadm Rennes_STA" + } + } + }, + { + "path-route-object": { + "index": 4, + "num-unnum-hop": { + "node-id": "fiber (Rennes_STA → Stbrieuc)-F057", + "link-tp-id": "fiber (Rennes_STA → Stbrieuc)-F057" + } + } + }, + { + "path-route-object": { + "index": 5, + "num-unnum-hop": { + "node-id": "Edfa0_fiber (Rennes_STA → Stbrieuc)-F057", + "link-tp-id": "Edfa0_fiber (Rennes_STA → Stbrieuc)-F057" + } + } + }, + { + "path-route-object": { + "index": 6, + "num-unnum-hop": { + "node-id": "fiber (Stbrieuc → Lannion_CAS)-F056", + "link-tp-id": "fiber (Stbrieuc → Lannion_CAS)-F056" + } + } + }, + { + "path-route-object": { + "index": 7, + "num-unnum-hop": { + "node-id": "Edfa0_fiber (Stbrieuc → Lannion_CAS)-F056", + "link-tp-id": "Edfa0_fiber (Stbrieuc → Lannion_CAS)-F056" + } + } + }, + { + "path-route-object": { + "index": 8, + "num-unnum-hop": { + "node-id": "roadm Lannion_CAS", + "link-tp-id": "roadm Lannion_CAS" + } + } + }, + { + "path-route-object": { + "index": 9, + "num-unnum-hop": { + "node-id": "trx Lannion_CAS", + "link-tp-id": "trx Lannion_CAS" + } + } + }, + { + "path-route-object": { + "index": 10, + "transponder": { + "transponder-type": "vendorA_trx-type1", + "transponder-mode": "mode 2" + } + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/tests/data/testTopology_response_expected.csv b/tests/data/testTopology_response_expected.csv new file mode 100644 index 000000000..34ff9cd1f --- /dev/null +++ b/tests/data/testTopology_response_expected.csv @@ -0,0 +1,6 @@ +response-id,source,destination,path_bandwidth,Pass?,nb of tsp pairs,total cost,transponder-type,transponder-mode,OSNR-0.1nm,SNR-0.1nm,SNR-bandwidth,baud rate (Gbaud),input power (dBm),path +0,trx Lorient_KMA,trx Vannes_KBE,100.0,True,1,1,Voyager,mode 1,30.84,30.84,26.75,32.0,0.0,trx Lorient_KMA | roadm Lorient_KMA | Edfa1_roadm Lorient_KMA | fiber (Lorient_KMA → Vannes_KBE)-F055 | Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055 | roadm Vannes_KBE | trx Vannes_KBE +1,trx Brest_KLA,trx Vannes_KBE,0.0,True,0,0,Voyager,mode 1,22.65,22.11,18.03,32.0,1.0,trx Brest_KLA | roadm Brest_KLA | Edfa0_roadm Brest_KLA | fiber (Brest_KLA → Morlaix)-F060 | east fused spans in Morlaix | fiber (Morlaix → Lannion_CAS)-F059 | west edfa in Lannion_CAS to Morlaix | roadm Lannion_CAS | east edfa in Lannion_CAS to Corlay | fiber (Lannion_CAS → Corlay)-F061 | west fused spans in Corlay | fiber (Corlay → Loudeac)-F010 | west fused spans in Loudeac | fiber (Loudeac → Lorient_KMA)-F054 | Edfa0_fiber (Loudeac → Lorient_KMA)-F054 | roadm Lorient_KMA | Edfa1_roadm Lorient_KMA | fiber (Lorient_KMA → Vannes_KBE)-F055 | Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055 | roadm Vannes_KBE | trx Vannes_KBE +3,trx Lannion_CAS,trx Rennes_STA,60.0,True,1,1,vendorA_trx-type1,mode 1,28.29,25.85,21.77,32.0,1.0,trx Lannion_CAS | roadm Lannion_CAS | east edfa in Lannion_CAS to Stbrieuc | fiber (Lannion_CAS → Stbrieuc)-F056 | east edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Rennes_STA)-F057 | Edfa0_fiber (Stbrieuc → Rennes_STA)-F057 | roadm Rennes_STA | trx Rennes_STA +4,trx Rennes_STA,trx Lannion_CAS,150.0,True,1,1,vendorA_trx-type1,mode 2,22.27,22.15,15.05,64.0,0.0,trx Rennes_STA | roadm Rennes_STA | Edfa1_roadm Rennes_STA | fiber (Rennes_STA → Ploermel)- | east edfa in Ploermel to Vannes_KBE | fiber (Ploermel → Vannes_KBE)- | Edfa0_fiber (Ploermel → Vannes_KBE)- | roadm Vannes_KBE | Edfa0_roadm Vannes_KBE | fiber (Vannes_KBE → Lorient_KMA)-F055 | Edfa0_fiber (Vannes_KBE → Lorient_KMA)-F055 | roadm Lorient_KMA | Edfa0_roadm Lorient_KMA | fiber (Lorient_KMA → Loudeac)-F054 | east fused spans in Loudeac | fiber (Loudeac → Corlay)-F010 | east fused spans in Corlay | fiber (Corlay → Lannion_CAS)-F061 | west edfa in Lannion_CAS to Corlay | roadm Lannion_CAS | trx Lannion_CAS +5,trx Rennes_STA,trx Lannion_CAS,20.0,True,1,1,vendorA_trx-type1,mode 2,30.79,28.77,21.68,64.0,3.0,trx Rennes_STA | roadm Rennes_STA | Edfa0_roadm Rennes_STA | fiber (Rennes_STA → Stbrieuc)-F057 | Edfa0_fiber (Rennes_STA → Stbrieuc)-F057 | fiber (Stbrieuc → Lannion_CAS)-F056 | Edfa0_fiber (Stbrieuc → Lannion_CAS)-F056 | roadm Lannion_CAS | trx Lannion_CAS diff --git a/tests/test_parser.py b/tests/test_parser.py index 45975c4fb..06904acfb 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -17,9 +17,11 @@ from gnpy.core.service_sheet import convert_service_sheet from gnpy.core.equipment import load_equipment, automatic_nch from gnpy.core.network import load_network +from gnpy.core.request import jsontocsv from pathlib import Path import filecmp from os import unlink +from pandas import read_csv TEST_DIR = Path(__file__).parent DATA_DIR = TEST_DIR / 'data' @@ -150,3 +152,54 @@ def test_excel_service_json_generation(xls_input, expected_json_output): assert not results.synchronizations.missing assert not results.synchronizations.extra assert not results.synchronizations.different + +# test xls answers creation +@pytest.mark.parametrize('json_input, csv_output', { + DATA_DIR / 'testTopology_response.json': DATA_DIR / 'testTopology_response', +}.items()) +def test_csv_response_generation(json_input, csv_output): + """ tests if generated csv is consistant with expected generation + same columns (order not important) + """ + with open(json_input) as jsonfile: + json_data = load(jsonfile) + equipment = load_equipment(eqpt_filename) + csv_filename = str(csv_output)+'.csv' + with open(csv_filename, 'w', encoding='utf-8') as fcsv: + jsontocsv(json_data, equipment, fcsv) + + expected_csv_filename = str(csv_output)+'_expected.csv' + + # expected header + # csv_header = \ + # [ + # 'response-id', + # 'source', + # 'destination', + # 'path_bandwidth', + # 'Pass?', + # 'nb of tsp pairs', + # 'total cost', + # 'transponder-type', + # 'transponder-mode', + # 'OSNR-0.1nm', + # 'SNR-0.1nm', + # 'SNR-bandwidth', + # 'baud rate (Gbaud)', + # 'input power (dBm)', + # 'path' + # ] + + resp = read_csv(csv_filename) + unlink(csv_filename) + expected_resp = read_csv(expected_csv_filename) + resp_header = list(resp.head(0)) + # check that headers are the same + assert resp_header == list(expected_resp.head(0)) + + # for each header checks that the output are as expected + resp.sort_values(by=['response-id']) + expected_resp.sort_values(by=['response-id']) + + for column in expected_resp: + assert list(resp[column]) == list(expected_resp[column]) From b388d143fd6fc8fd92ab5a91b3087da25716309e Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 19 Aug 2019 10:24:57 +0100 Subject: [PATCH 25/35] change assert to raise AssertionError use if not condition: raise AssertionError() instead of assert condition according to codacy recommendation Signed-off-by: EstherLerouzic --- tests/test_parser.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_parser.py b/tests/test_parser.py index 06904acfb..00bb07349 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -202,4 +202,7 @@ def test_csv_response_generation(json_input, csv_output): expected_resp.sort_values(by=['response-id']) for column in expected_resp: - assert list(resp[column]) == list(expected_resp[column]) + if list(resp[column]) != list(expected_resp[column]): + raise AssertionError('results are different') + + From e55cea776e1ce4e27a22380bf0a2ee3c7fec6af2 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 19 Aug 2019 10:28:52 +0100 Subject: [PATCH 26/35] improve code quality remove unused import, use correct indents, remove extra spaces add function description Signed-off-by: EstherLerouzic --- gnpy/core/service_sheet.py | 6 +-- tests/test_parser.py | 75 +++++++++++++++++++++++--------------- 2 files changed, 48 insertions(+), 33 deletions(-) diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index 8e3476499..cc7415267 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -154,7 +154,7 @@ def pathrequest(self): if self.nodes_list: req_dictionnary['explicit-route-objects'] = {} - temp = {'route-object-include-exclude' : [ + temp = {'route-object-include-exclude' : [ {'explicit-route-usage': 'route-include-ero', 'index': self.nodes_list.index(node), 'num-unnum-hop': { @@ -163,8 +163,8 @@ def pathrequest(self): 'hop-type': f'{self.loose}', } } - for node in self.nodes_list ] - } + for node in self.nodes_list] + } req_dictionnary['explicit-route-objects'] = temp if self.path_bandwidth is not None: req_dictionnary['path-constraints']['te-bandwidth']['path_bandwidth'] = self.path_bandwidth diff --git a/tests/test_parser.py b/tests/test_parser.py index 00bb07349..06f853869 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -3,14 +3,21 @@ # @Author: Esther Le Rouzic # @Date: 2018-06-15 -from gnpy.core.elements import Edfa -import numpy as np +""" Adding tests to check the parser non regression + convention of naming of test files: + - ..._expected.json for the reference output + tests: + - generation of topology json + - reading of Eqpt sheet w and W/ power mode + - consistency of autodesign + - generation of service list based on service sheet + - writing of results in csv + - writing of results in json (same keys) +""" + from json import load import pytest -from gnpy.core import network_from_json -from gnpy.core.elements import Transceiver, Fiber, Edfa -from gnpy.core.utils import lin2db, db2lin -from gnpy.core.info import SpectralInformation, Channel, Power +from gnpy.core.utils import lin2db from gnpy.core.network import save_network, build_network from tests.compare import compare_networks, compare_services from gnpy.core.convert import convert_file @@ -19,7 +26,6 @@ from gnpy.core.network import load_network from gnpy.core.request import jsontocsv from pathlib import Path -import filecmp from os import unlink from pandas import read_csv @@ -27,16 +33,14 @@ DATA_DIR = TEST_DIR / 'data' eqpt_filename = DATA_DIR / 'eqpt_config.json' -# adding tests to check the parser non regression -# convention of naming of test files: -# -# - ..._expected.json for the reference output @pytest.mark.parametrize('xls_input,expected_json_output', { DATA_DIR / 'CORONET_Global_Topology.xls': DATA_DIR / 'CORONET_Global_Topology_expected.json', DATA_DIR / 'testTopology.xls': DATA_DIR / 'testTopology_expected.json', - }.items()) + }.items()) def test_excel_json_generation(xls_input, expected_json_output): + """ tests generation of topology json + """ convert_file(xls_input) actual_json_output = xls_input.with_suffix('.json') @@ -57,20 +61,25 @@ def test_excel_json_generation(xls_input, expected_json_output): # assume xls entries # test that the build network gives correct results in gain mode -# -@pytest.mark.parametrize('xls_input,expected_json_output', { - DATA_DIR / 'CORONET_Global_Topology.xls': DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json', - DATA_DIR / 'testTopology.xls': DATA_DIR / 'testTopology_auto_design_expected.json', - }.items()) + +@pytest.mark.parametrize('xls_input,expected_json_output', + {DATA_DIR / 'CORONET_Global_Topology.xls':\ + DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json', + DATA_DIR / 'testTopology.xls':\ + DATA_DIR / 'testTopology_auto_design_expected.json', + }.items()) def test_auto_design_generation_fromxlsgainmode(xls_input, expected_json_output): + """ tests generation of topology json + test that the build network gives correct results in gain mode + """ equipment = load_equipment(eqpt_filename) - network = load_network(xls_input,equipment) - # in order to test the Eqpt sheet and load gain target, change the power-mode to False (to be in gain mode) + network = load_network(xls_input, equipment) + # in order to test the Eqpt sheet and load gain target, + # change the power-mode to False (to be in gain mode) equipment['Span']['default'].power_mode = False # Build the network once using the default power defined in SI in eqpt config - + p_db = equipment['SI']['default'].power_dbm - p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) build_network(network, equipment, p_db, p_total_db) @@ -94,19 +103,23 @@ def test_auto_design_generation_fromxlsgainmode(xls_input, expected_json_output) assert not results.connections.different #test that autodesign creates same file as an input file already autodesigned -@pytest.mark.parametrize('json_input,expected_json_output', { - DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json': DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json', - DATA_DIR / 'testTopology_auto_design_expected.json': DATA_DIR / 'testTopology_auto_design_expected.json', - }.items()) +@pytest.mark.parametrize('json_input,expected_json_output', + {DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json':\ + DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json', + DATA_DIR / 'testTopology_auto_design_expected.json':\ + DATA_DIR / 'testTopology_auto_design_expected.json', + }.items()) def test_auto_design_generation_fromjson(json_input, expected_json_output): + """test that autodesign creates same file as an input file already autodesigned + """ equipment = load_equipment(eqpt_filename) - network = load_network(json_input,equipment) - # in order to test the Eqpt sheet and load gain target, change the power-mode to False (to be in gain mode) + network = load_network(json_input, equipment) + # in order to test the Eqpt sheet and load gain target, + # change the power-mode to False (to be in gain mode) equipment['Span']['default'].power_mode = False # Build the network once using the default power defined in SI in eqpt config - + p_db = equipment['SI']['default'].power_dbm - p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) build_network(network, equipment, p_db, p_total_db) @@ -133,8 +146,10 @@ def test_auto_design_generation_fromjson(json_input, expected_json_output): @pytest.mark.parametrize('xls_input,expected_json_output', { DATA_DIR / 'testTopology.xls': DATA_DIR / 'testTopology_services_expected.json', -}.items()) + }.items()) def test_excel_service_json_generation(xls_input, expected_json_output): + """ test services creation + """ convert_service_sheet(xls_input, eqpt_filename) actual_json_output = f'{str(xls_input)[:-4]}_services.json' From 715baf2a1cff224ab549860989ae5e5e23c44491 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 19 Aug 2019 10:30:26 +0100 Subject: [PATCH 27/35] correct compare response header test to support any order previous test assumed same order for header fields. order the headers in the same way in order to support different types of order Signed-off-by: EstherLerouzic --- tests/test_parser.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_parser.py b/tests/test_parser.py index 06f853869..7a931de4e 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -209,8 +209,10 @@ def test_csv_response_generation(json_input, csv_output): unlink(csv_filename) expected_resp = read_csv(expected_csv_filename) resp_header = list(resp.head(0)) + expected_resp_header = list(expected_resp.head(0)) # check that headers are the same - assert resp_header == list(expected_resp.head(0)) + if not resp_header.sort() == expected_resp_header.sort(): + raise AssertionError('headers are differents') # for each header checks that the output are as expected resp.sort_values(by=['response-id']) From 48b7d71f0225f1f1b03911f88ee311afc761d7c6 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 19 Aug 2019 10:32:58 +0100 Subject: [PATCH 28/35] add a test on json response generation create a json response based on test file and compare it to expected response. comparison first checks that there is no missing or extra key and then compares keys Signed-off-by: EstherLerouzic --- tests/test_parser.py | 71 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/tests/test_parser.py b/tests/test_parser.py index 7a931de4e..2517fe70c 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -24,7 +24,11 @@ from gnpy.core.service_sheet import convert_service_sheet from gnpy.core.equipment import load_equipment, automatic_nch from gnpy.core.network import load_network -from gnpy.core.request import jsontocsv +from gnpy.core.request import (jsontocsv, requests_aggregation, + compute_path_dsjctn, Result_element) +from examples.path_requests_run import (requests_from_json, disjunctions_from_json, + correct_route_list, correct_disjn, + compute_path_with_disjunction) from pathlib import Path from os import unlink from pandas import read_csv @@ -223,3 +227,68 @@ def test_csv_response_generation(json_input, csv_output): raise AssertionError('results are different') +def compare_response(exp_resp, act_resp): + """ False if the keys are different in the nested dicts as well + """ + print(exp_resp) + print(act_resp) + test = True + for key in act_resp.keys(): + print(key) + if not key in exp_resp.keys(): + print(key) + return False + if isinstance(act_resp[key], dict): + test = compare_response(exp_resp[key], act_resp[key]) + if test: + for key in exp_resp.keys(): + if not key in act_resp.keys(): + print(key) + return False + if isinstance(exp_resp[key], dict): + test = compare_response(exp_resp[key], act_resp[key]) + # at this point exp_resp and act_resp have the same keys. Check if their values are the same + for key in act_resp.keys(): + if not isinstance(act_resp[key], dict): + if exp_resp[key] != act_resp[key]: + return False + return test + + +# test json answers creation +@pytest.mark.parametrize('xls_input, expected_response_file', { + DATA_DIR / 'testTopology.xls': DATA_DIR / 'testTopology_response.json', +}.items()) +def test_json_response_generation(xls_input, expected_response_file): + """ tests if json response is correctly generated for all combinations of requests + """ + data = convert_service_sheet(xls_input, eqpt_filename) + equipment = load_equipment(eqpt_filename) + network = load_network(xls_input, equipment) + p_db = equipment['SI']['default'].power_dbm + + p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ + equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) + build_network(network, equipment, p_db, p_total_db) + rqs = requests_from_json(data, equipment) + rqs = correct_route_list(network, rqs) + dsjn = disjunctions_from_json(data) + dsjn = correct_disjn(dsjn) + rqs, dsjn = requests_aggregation(rqs, dsjn) + pths = compute_path_dsjctn(network, equipment, rqs, dsjn) + propagatedpths = compute_path_with_disjunction(network, equipment, rqs, pths) + result = [] + for i, pth in enumerate(propagatedpths): + result.append(Result_element(rqs[i], pth)) + temp = { + 'response': [n.json for n in result] + } + # load expected result and compare keys + # (not values at this stage) + with open(expected_response_file) as jsonfile: + expected = load(jsonfile) + + for i, response in enumerate(temp['response']): + if not compare_response(expected['response'][i], response): + raise AssertionError() + From 0366fc2956f5168b8a67cede2b0445f9acc9375e Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 19 Aug 2019 10:39:59 +0100 Subject: [PATCH 29/35] Adding a no-path case for test coverage add a no path case (request 6) in requests and expected responses. response is also generated if path is not feasible: checks that it is correctly handled in csv and json responses Signed-off-by: EstherLerouzic --- gnpy/core/service_sheet.py | 2 +- tests/data/testTopology.xls | Bin 16896 -> 17408 bytes tests/data/testTopology_response.json | 4 +++ tests/data/testTopology_response_expected.csv | 13 +++++----- .../data/testTopology_services_expected.json | 24 ++++++++++++++++++ tests/test_parser.py | 10 +++++--- 6 files changed, 43 insertions(+), 10 deletions(-) diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index cc7415267..c422d774f 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -198,7 +198,7 @@ def convert_service_sheet(input_filename, eqpt_filename, output_filename='', fil # print(json_filename) # if there is no sync vector , do not write any synchronization synchro = [n.json[1] for n in req if n.json[1] is not None] - if synchro : + if synchro: data = { 'path-request': [n.json[0] for n in req], 'synchronization': synchro diff --git a/tests/data/testTopology.xls b/tests/data/testTopology.xls index 0005997b5919ca64c7c1f53680fff17b8d4b8d22..94526f9f366933c9cef62271c66f18617c3303f6 100644 GIT binary patch delta 896 zcmY*X&1w@-7(L&epG-1;siwBkwuvIts)#X*SXzsl?%b#w#mx`}DVCsBH;T4V3+@bF z+==uBEZXq_Tnva0(3L)bt2}_v#_x`4@6B9>nRCzOeBU|W-FQPAZ>e`NtYP?+fvK-o z&WY*?-q;@u2I`#|{@2wv?TVW=S?R93>Ylnk!CP=}ZKdUt)mLENQvWXZ! zj{*2 z!A}>bi5f|yPpfKXb)nRaYHqI9ndx{F;}Dg+y92cf7?b?es-?l~EUm{z7fScJft8D` zSl6{+A@el0Ihm}ivBR!RzQ8^bqc0EM2wz0;2>ONq+vOOtGuxhg);g-92)U#YMXy1$ z-uJ2_!S_Rr^WYB`-xjdQ@?o>An0S5ToKD5+j_2Iovwr zY@EeyM*meDMi=M+`Fx4rldQGw$hF3?hy&+`hBu9GMLXSEOi1?Z$(_q>t2B?R>cH<` m@8s%2-RsW%{Bmz3m)Q delta 792 zcmY*X&o2W(6n->aVaLv}LacER752I#mg4)Nyidc976 z4g<7y(|S95z^cXzXFPZ{SIu6(m5#+=W@&9@vjPq{J+xuK^u~I*0x7tci|#qgSG_B9 z*3YDXV{`}e7>>|Ym}4YNDlCvxkDFkE@2GejfQ7+#cFaOjVE|%eG;qY@@}s{ZpK{xy zzNZdQL0tf+0$M>cA}n{US+tqOY$6Qa`)Am0Mk`D_I~pe87b#UofPm0am?VX1g=1($ zkH=BEuj#PBYKSKw(F8S(z>b7I4_sT47g{_Q>yc6yhY9m~~X-m-@W d&c51TncO#qgU8|>XPt7Zbif}*gU^yFegLjob~^w7 diff --git a/tests/data/testTopology_response.json b/tests/data/testTopology_response.json index d5b9948b2..ccc9d4e44 100644 --- a/tests/data/testTopology_response.json +++ b/tests/data/testTopology_response.json @@ -857,6 +857,10 @@ } ] } + }, + { + "response-id": "6", + "no-path": "Response without path information, due to failure performing the path computation" } ] } \ No newline at end of file diff --git a/tests/data/testTopology_response_expected.csv b/tests/data/testTopology_response_expected.csv index 34ff9cd1f..1f3cf7523 100644 --- a/tests/data/testTopology_response_expected.csv +++ b/tests/data/testTopology_response_expected.csv @@ -1,6 +1,7 @@ -response-id,source,destination,path_bandwidth,Pass?,nb of tsp pairs,total cost,transponder-type,transponder-mode,OSNR-0.1nm,SNR-0.1nm,SNR-bandwidth,baud rate (Gbaud),input power (dBm),path -0,trx Lorient_KMA,trx Vannes_KBE,100.0,True,1,1,Voyager,mode 1,30.84,30.84,26.75,32.0,0.0,trx Lorient_KMA | roadm Lorient_KMA | Edfa1_roadm Lorient_KMA | fiber (Lorient_KMA → Vannes_KBE)-F055 | Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055 | roadm Vannes_KBE | trx Vannes_KBE -1,trx Brest_KLA,trx Vannes_KBE,0.0,True,0,0,Voyager,mode 1,22.65,22.11,18.03,32.0,1.0,trx Brest_KLA | roadm Brest_KLA | Edfa0_roadm Brest_KLA | fiber (Brest_KLA → Morlaix)-F060 | east fused spans in Morlaix | fiber (Morlaix → Lannion_CAS)-F059 | west edfa in Lannion_CAS to Morlaix | roadm Lannion_CAS | east edfa in Lannion_CAS to Corlay | fiber (Lannion_CAS → Corlay)-F061 | west fused spans in Corlay | fiber (Corlay → Loudeac)-F010 | west fused spans in Loudeac | fiber (Loudeac → Lorient_KMA)-F054 | Edfa0_fiber (Loudeac → Lorient_KMA)-F054 | roadm Lorient_KMA | Edfa1_roadm Lorient_KMA | fiber (Lorient_KMA → Vannes_KBE)-F055 | Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055 | roadm Vannes_KBE | trx Vannes_KBE -3,trx Lannion_CAS,trx Rennes_STA,60.0,True,1,1,vendorA_trx-type1,mode 1,28.29,25.85,21.77,32.0,1.0,trx Lannion_CAS | roadm Lannion_CAS | east edfa in Lannion_CAS to Stbrieuc | fiber (Lannion_CAS → Stbrieuc)-F056 | east edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Rennes_STA)-F057 | Edfa0_fiber (Stbrieuc → Rennes_STA)-F057 | roadm Rennes_STA | trx Rennes_STA -4,trx Rennes_STA,trx Lannion_CAS,150.0,True,1,1,vendorA_trx-type1,mode 2,22.27,22.15,15.05,64.0,0.0,trx Rennes_STA | roadm Rennes_STA | Edfa1_roadm Rennes_STA | fiber (Rennes_STA → Ploermel)- | east edfa in Ploermel to Vannes_KBE | fiber (Ploermel → Vannes_KBE)- | Edfa0_fiber (Ploermel → Vannes_KBE)- | roadm Vannes_KBE | Edfa0_roadm Vannes_KBE | fiber (Vannes_KBE → Lorient_KMA)-F055 | Edfa0_fiber (Vannes_KBE → Lorient_KMA)-F055 | roadm Lorient_KMA | Edfa0_roadm Lorient_KMA | fiber (Lorient_KMA → Loudeac)-F054 | east fused spans in Loudeac | fiber (Loudeac → Corlay)-F010 | east fused spans in Corlay | fiber (Corlay → Lannion_CAS)-F061 | west edfa in Lannion_CAS to Corlay | roadm Lannion_CAS | trx Lannion_CAS -5,trx Rennes_STA,trx Lannion_CAS,20.0,True,1,1,vendorA_trx-type1,mode 2,30.79,28.77,21.68,64.0,3.0,trx Rennes_STA | roadm Rennes_STA | Edfa0_roadm Rennes_STA | fiber (Rennes_STA → Stbrieuc)-F057 | Edfa0_fiber (Rennes_STA → Stbrieuc)-F057 | fiber (Stbrieuc → Lannion_CAS)-F056 | Edfa0_fiber (Stbrieuc → Lannion_CAS)-F056 | roadm Lannion_CAS | trx Lannion_CAS +response-id,source,destination,path_bandwidth,Pass?,nb of tsp pairs,total cost,transponder-type,transponder-mode,OSNR-0.1nm,SNR-0.1nm,SNR-bandwidth,baud rate (Gbaud),input power (dBm),path +0,trx Lorient_KMA,trx Vannes_KBE,100.0,True,1,1,Voyager,mode 1,30.84,30.84,26.75,32.0,0.0,trx Lorient_KMA | roadm Lorient_KMA | Edfa1_roadm Lorient_KMA | fiber (Lorient_KMA → Vannes_KBE)-F055 | Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055 | roadm Vannes_KBE | trx Vannes_KBE +1,trx Brest_KLA,trx Vannes_KBE,0.0,True,0,0,Voyager,mode 1,22.65,22.11,18.03,32.0,1.0,trx Brest_KLA | roadm Brest_KLA | Edfa0_roadm Brest_KLA | fiber (Brest_KLA → Morlaix)-F060 | east fused spans in Morlaix | fiber (Morlaix → Lannion_CAS)-F059 | west edfa in Lannion_CAS to Morlaix | roadm Lannion_CAS | east edfa in Lannion_CAS to Corlay | fiber (Lannion_CAS → Corlay)-F061 | west fused spans in Corlay | fiber (Corlay → Loudeac)-F010 | west fused spans in Loudeac | fiber (Loudeac → Lorient_KMA)-F054 | Edfa0_fiber (Loudeac → Lorient_KMA)-F054 | roadm Lorient_KMA | Edfa1_roadm Lorient_KMA | fiber (Lorient_KMA → Vannes_KBE)-F055 | Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055 | roadm Vannes_KBE | trx Vannes_KBE +3,trx Lannion_CAS,trx Rennes_STA,60.0,True,1,1,vendorA_trx-type1,mode 1,28.29,25.85,21.77,32.0,1.0,trx Lannion_CAS | roadm Lannion_CAS | east edfa in Lannion_CAS to Stbrieuc | fiber (Lannion_CAS → Stbrieuc)-F056 | east edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Rennes_STA)-F057 | Edfa0_fiber (Stbrieuc → Rennes_STA)-F057 | roadm Rennes_STA | trx Rennes_STA +4,trx Rennes_STA,trx Lannion_CAS,150.0,True,1,1,vendorA_trx-type1,mode 2,22.27,22.15,15.05,64.0,0.0,trx Rennes_STA | roadm Rennes_STA | Edfa1_roadm Rennes_STA | fiber (Rennes_STA → Ploermel)- | east edfa in Ploermel to Vannes_KBE | fiber (Ploermel → Vannes_KBE)- | Edfa0_fiber (Ploermel → Vannes_KBE)- | roadm Vannes_KBE | Edfa0_roadm Vannes_KBE | fiber (Vannes_KBE → Lorient_KMA)-F055 | Edfa0_fiber (Vannes_KBE → Lorient_KMA)-F055 | roadm Lorient_KMA | Edfa0_roadm Lorient_KMA | fiber (Lorient_KMA → Loudeac)-F054 | east fused spans in Loudeac | fiber (Loudeac → Corlay)-F010 | east fused spans in Corlay | fiber (Corlay → Lannion_CAS)-F061 | west edfa in Lannion_CAS to Corlay | roadm Lannion_CAS | trx Lannion_CAS +5,trx Rennes_STA,trx Lannion_CAS,20.0,True,1,1,vendorA_trx-type1,mode 2,30.79,28.77,21.68,64.0,3.0,trx Rennes_STA | roadm Rennes_STA | Edfa0_roadm Rennes_STA | fiber (Rennes_STA → Stbrieuc)-F057 | Edfa0_fiber (Rennes_STA → Stbrieuc)-F057 | fiber (Stbrieuc → Lannion_CAS)-F056 | Edfa0_fiber (Stbrieuc → Lannion_CAS)-F056 | roadm Lannion_CAS | trx Lannion_CAS +6,,,,False,0,,,,,,,,, diff --git a/tests/data/testTopology_services_expected.json b/tests/data/testTopology_services_expected.json index 48a020b49..6d3d5b2f4 100644 --- a/tests/data/testTopology_services_expected.json +++ b/tests/data/testTopology_services_expected.json @@ -159,6 +159,30 @@ "path_bandwidth": 20000000000.0 } } + }, + { + "request-id": "6", + "source": "trx Lannion_CAS", + "destination": "trx a", + "src-tp-id": "trx Lannion_CAS", + "dst-tp-id": "trx a", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "vendorA_trx-type1", + "trx_mode": "mode 2", + "effective-freq-slot": [ + { + "N": "null", + "M": "null" + } + ], + "spacing": 75000000000.0, + "max-nb-of-channel": null, + "output-power": null, + "path_bandwidth": 100000000000.0 + } + } } ], "synchronization": [ diff --git a/tests/test_parser.py b/tests/test_parser.py index 2517fe70c..4ea18dd68 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -215,7 +215,9 @@ def test_csv_response_generation(json_input, csv_output): resp_header = list(resp.head(0)) expected_resp_header = list(expected_resp.head(0)) # check that headers are the same - if not resp_header.sort() == expected_resp_header.sort(): + if resp_header.sort() != expected_resp_header.sort(): + print(resp_header.sort()) + print(expected_resp_header.sort()) raise AssertionError('headers are differents') # for each header checks that the output are as expected @@ -223,10 +225,12 @@ def test_csv_response_generation(json_input, csv_output): expected_resp.sort_values(by=['response-id']) for column in expected_resp: - if list(resp[column]) != list(expected_resp[column]): + if list(resp[column].fillna('')) != list(expected_resp[column].fillna('')): + print(list(resp[column])) + print(list(expected_resp[column])) + print(type(list(resp[column])[-1])) raise AssertionError('results are different') - def compare_response(exp_resp, act_resp): """ False if the keys are different in the nested dicts as well """ From 6a7a04ebb11488c06cab83d85382406ed61f5978 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 19 Aug 2019 11:24:58 +0100 Subject: [PATCH 30/35] syntax correction reordering imports call according to pylint3 + extra line removed Signed-off-by: EstherLerouzic --- tests/test_parser.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/test_parser.py b/tests/test_parser.py index 4ea18dd68..1ac0975ff 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -16,10 +16,13 @@ """ from json import load +from pathlib import Path +from os import unlink +from pandas import read_csv import pytest +from tests.compare import compare_networks, compare_services from gnpy.core.utils import lin2db from gnpy.core.network import save_network, build_network -from tests.compare import compare_networks, compare_services from gnpy.core.convert import convert_file from gnpy.core.service_sheet import convert_service_sheet from gnpy.core.equipment import load_equipment, automatic_nch @@ -29,9 +32,6 @@ from examples.path_requests_run import (requests_from_json, disjunctions_from_json, correct_route_list, correct_disjn, compute_path_with_disjunction) -from pathlib import Path -from os import unlink -from pandas import read_csv TEST_DIR = Path(__file__).parent DATA_DIR = TEST_DIR / 'data' @@ -295,4 +295,3 @@ def test_json_response_generation(xls_input, expected_response_file): for i, response in enumerate(temp['response']): if not compare_response(expected['response'][i], response): raise AssertionError() - From 424e5a47865d8e5c6a76f5f1a02e1b483255f09e Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 19 Aug 2019 13:40:57 +0100 Subject: [PATCH 31/35] change variable n to nel to conform to '[a-z_][a-z0-9_]{2,30}$' pattern from codacy report Signed-off-by: EstherLerouzic --- gnpy/core/request.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index de579f78c..92e180c6b 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -126,20 +126,20 @@ def pathresult(self): else: index = 0 pro_list = [] - for n in self.computed_path: + for nel in self.computed_path: temp = { 'path-route-object': { 'index': index, 'num-unnum-hop': { - 'node-id': n.uid, - 'link-tp-id': n.uid, + 'node-id': nel.uid, + 'link-tp-id': nel.uid, # TODO change index in order to insert transponder attribute } } } pro_list.append(temp) index += 1 - if isinstance(n, Transceiver): + if isinstance(nel, Transceiver): temp = { 'path-route-object': { 'index': index, From f990a6c1bec05cf639f41652d350437bfac0e8e2 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 16 Sep 2019 09:49:50 +0100 Subject: [PATCH 32/35] Correct some remaining strict loose into STRICT LOOSE Signed-off-by: EstherLerouzic --- gnpy/core/request.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 92e180c6b..8742e2629 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -247,8 +247,11 @@ def compute_constrained_path(network, req): candidate.sort(key=lambda x: sum(network.get_edge_data(x[i],x[i+1])['weight'] for i in range(len(x)-2))) total_path = candidate[0] else: - if req.loose_list[req.nodes_list.index(n)] == 'loose': - print(f'\x1b[1;33;40m'+f'Request {req.request_id} could not find a path crossing {nodes_list} in network topology'+ '\x1b[0m') + # TODO: better account for individual oose and strict node + #to ease: suppose that one strict makes the whole liste strict + if 'STRICT' in req.loose_list[:-6]: + print(f'\x1b[1;33;40m'+f'Request {req.request_id} could not find a path crossing' +\ + f'{nodes_list} in network topology'+ '\x1b[0m') print(f'constraint ignored') total_path = dijkstra_path(network, source, destination, weight = 'weight') else: @@ -713,9 +716,10 @@ def __init__(self, req, pth, simplepth): testispartok = False #break else: - if 'loose' in allpaths[id(p)].req.loose_list: + if 'LOOSE' in allpaths[id(p)].req.loose_list: logger.info(f'Could not apply route constraint'+ - f'{allpaths[id(p)].req.nodes_list} on request {allpaths[id(p)].req.request_id}') + f'{allpaths[id(p)].req.nodes_list} on request' +\ + f' {allpaths[id(p)].req.request_id}') else : logger.info(f'removing last solution from candidate paths\n{sol}') testispartok = False @@ -752,7 +756,7 @@ def __init__(self, req, pth, simplepth): for req in pathreqlist : req.nodes_list.append(req.destination) # we assume that the destination is a strict constraint - req.loose_list.append('strict') + req.loose_list.append('STRICT') if req in pathreqlist_simple: path_res_list.append(compute_constrained_path(network, req)) else: From 2ba29a78c50e299acfa655335e5765b84903adcf Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 16 Sep 2019 12:13:53 +0100 Subject: [PATCH 33/35] Bug: constraint not correctly interpreted if name of the constraint input from user is not part of networks names (typically in the case of excel input), then the program try to find a name that is clode to the user name and that is in the network list of names. This list must not include trnasponder names (because transponder end points are already listed as constraints and transponder in the middle of a path are not supported yet) This will be improved in the PR Ila names in constraints #278 Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 4 +++- gnpy/core/request.py | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index cc34b6b61..3b9acf2f0 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -227,8 +227,10 @@ def correct_route_list(network, pathreqlist): # replace possibly wrong name with a formated roadm name # print(n_id) if n_id not in anytype : + # find nodes name that include constraint among all possible names except + # transponders (not yet supported as constraints). nodes_suggestion = [uid for uid in anytype \ - if n_id.lower() in uid.lower()] + if n_id.lower() in uid.lower() and uid not in transponders] if pathreq.loose_list[i] == 'LOOSE': if len(nodes_suggestion)>0 : new_n = nodes_suggestion[0] diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 8742e2629..dd068ac3d 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -248,10 +248,11 @@ def compute_constrained_path(network, req): total_path = candidate[0] else: # TODO: better account for individual oose and strict node - #to ease: suppose that one strict makes the whole liste strict - if 'STRICT' in req.loose_list[:-6]: + # to ease: suppose that one strict makes the whole liste strict (except for the + # last node which is the transceiver) + if 'STRICT' not in req.loose_list[:-6]: print(f'\x1b[1;33;40m'+f'Request {req.request_id} could not find a path crossing' +\ - f'{nodes_list} in network topology'+ '\x1b[0m') + f'{[el.uid for el in nodes_list[:-6]]} in network topology'+ '\x1b[0m') print(f'constraint ignored') total_path = dijkstra_path(network, source, destination, weight = 'weight') else: From 89e28cc7be47acda00d2f5c70609eea20a1965b9 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Tue, 17 Sep 2019 08:53:21 +0100 Subject: [PATCH 34/35] Correct bug in parser: csv header comparison previous comparison was done on the result from .sort(), ie None. list.sort() method modifies the list in-place and returns None. Signed-off-by: EstherLerouzic --- tests/test_parser.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test_parser.py b/tests/test_parser.py index 1ac0975ff..80c87eb8e 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -215,9 +215,11 @@ def test_csv_response_generation(json_input, csv_output): resp_header = list(resp.head(0)) expected_resp_header = list(expected_resp.head(0)) # check that headers are the same - if resp_header.sort() != expected_resp_header.sort(): - print(resp_header.sort()) - print(expected_resp_header.sort()) + resp_header.sort() + expected_resp_header.sort() + if resp_header != expected_resp_header: + print(resp_header) + print(expected_resp_header) raise AssertionError('headers are differents') # for each header checks that the output are as expected From 87cc3dac00fa92a0d8efabf583b5b0ae50e89301 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 9 Oct 2019 15:29:20 +0100 Subject: [PATCH 35/35] Corrections with respect to second review Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 4 +--- gnpy/core/request.py | 28 +++++++++++++++++++--------- tests/test_parser.py | 21 ++++++++++----------- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 3b9acf2f0..3f3fa0eda 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -132,7 +132,6 @@ def disjunctions_from_json(json_data): temp_test = json_data['synchronization'] except KeyError: temp_test = [] - pass if temp_test: for snc in json_data['synchronization']: params = {} @@ -142,7 +141,6 @@ def disjunctions_from_json(json_data): params['node_diverse'] = 'node' in snc['svec']['disjointness'] params['disjunctions_req'] = snc['svec']['request-id-number'] disjunctions_list.append(Disjunction(**params)) - print(disjunctions_list) return disjunctions_list @@ -228,7 +226,7 @@ def correct_route_list(network, pathreqlist): # print(n_id) if n_id not in anytype : # find nodes name that include constraint among all possible names except - # transponders (not yet supported as constraints). + # transponders (not yet supported as constraints). nodes_suggestion = [uid for uid in anytype \ if n_id.lower() in uid.lower() and uid not in transponders] if pathreq.loose_list[i] == 'LOOSE': diff --git a/gnpy/core/request.py b/gnpy/core/request.py index dd068ac3d..a3be300ae 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -126,20 +126,20 @@ def pathresult(self): else: index = 0 pro_list = [] - for nel in self.computed_path: + for element in self.computed_path: temp = { 'path-route-object': { 'index': index, 'num-unnum-hop': { - 'node-id': nel.uid, - 'link-tp-id': nel.uid, + 'node-id': element.uid, + 'link-tp-id': element.uid, # TODO change index in order to insert transponder attribute } } } pro_list.append(temp) index += 1 - if isinstance(nel, Transceiver): + if isinstance(element, Transceiver): temp = { 'path-route-object': { 'index': index, @@ -247,16 +247,26 @@ def compute_constrained_path(network, req): candidate.sort(key=lambda x: sum(network.get_edge_data(x[i],x[i+1])['weight'] for i in range(len(x)-2))) total_path = candidate[0] else: - # TODO: better account for individual oose and strict node + # TODO: better account for individual loose and strict node # to ease: suppose that one strict makes the whole liste strict (except for the # last node which is the transceiver) - if 'STRICT' not in req.loose_list[:-6]: - print(f'\x1b[1;33;40m'+f'Request {req.request_id} could not find a path crossing' +\ - f'{[el.uid for el in nodes_list[:-6]]} in network topology'+ '\x1b[0m') + # if all nodes i n node_list are LOOSE constraint, skip the constraints and find + # a path w/o constraints, else there is no possible path + if nodes_list[:-len("STRICT")]: + print(f'\x1b[1;33;40m'+f'Request {req.request_id} could not find a path crossing ' +\ + f'{[el.uid for el in nodes_list[:-len("STRICT")]]} in network topology'+ '\x1b[0m') + else: + print(f'\x1b[1;33;40m'+f'User include_node constraints could not be applied ' +\ + f'(invalid names specified)'+ '\x1b[0m') + if 'STRICT' not in req.loose_list[:-len('STRICT')]: + msg = f'\x1b[1;33;40m'+f'Request {req.request_id} could not find a path with user_' +\ + f'include node constraints' + '\x1b[0m' + logger.info(msg) print(f'constraint ignored') total_path = dijkstra_path(network, source, destination, weight = 'weight') else: - msg = f'\x1b[1;33;40m'+f'Request {req.request_id} could not find a path crossing {nodes_list}.\nNo path computed'+ '\x1b[0m' + msg = f'\x1b[1;33;40m'+f'Request {req.request_id} could not find a path with user ' +\ + f'include node constraints.\nNo path computed'+ '\x1b[0m' logger.critical(msg) print(msg) total_path = [] diff --git a/tests/test_parser.py b/tests/test_parser.py index 80c87eb8e..0fc6705c7 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -217,21 +217,21 @@ def test_csv_response_generation(json_input, csv_output): # check that headers are the same resp_header.sort() expected_resp_header.sort() - if resp_header != expected_resp_header: - print(resp_header) - print(expected_resp_header) - raise AssertionError('headers are differents') + print('headers are differents') + print(resp_header) + print(expected_resp_header) + assert resp_header == expected_resp_header # for each header checks that the output are as expected resp.sort_values(by=['response-id']) expected_resp.sort_values(by=['response-id']) for column in expected_resp: - if list(resp[column].fillna('')) != list(expected_resp[column].fillna('')): - print(list(resp[column])) - print(list(expected_resp[column])) - print(type(list(resp[column])[-1])) - raise AssertionError('results are different') + assert list(resp[column].fillna('')) == list(expected_resp[column].fillna('')) + print('results are different') + print(list(resp[column])) + print(list(expected_resp[column])) + print(type(list(resp[column])[-1])) def compare_response(exp_resp, act_resp): """ False if the keys are different in the nested dicts as well @@ -295,5 +295,4 @@ def test_json_response_generation(xls_input, expected_response_file): expected = load(jsonfile) for i, response in enumerate(temp['response']): - if not compare_response(expected['response'][i], response): - raise AssertionError() + assert compare_response(expected['response'][i], response)