mirror of
				https://github.com/Telecominfraproject/oopt-gnpy.git
				synced 2025-10-31 18:18:00 +00:00 
			
		
		
		
	Compare commits
	
		
			33 Commits
		
	
	
		
			v2.1
			...
			experiment
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 049b077ee4 | ||
|   | ab2080a805 | ||
|   | 8ab54e76df | ||
|   | 0465397b1d | ||
|   | f015c6abed | ||
|   | 71293c1c18 | ||
|   | bd7c70f902 | ||
|   | 20c92d4338 | ||
|   | f0158e7202 | ||
|   | 62408ddc98 | ||
|   | b4f87b36db | ||
|   | 9f49a115a1 | ||
|   | c7d2305589 | ||
|   | 5826a649de | ||
|   | fa826391f6 | ||
|   | 3481ba8ee3 | ||
|   | b4ab0b55de | ||
|   | 0370b45d8a | ||
|   | 468e689094 | ||
|   | aafd82b16d | ||
|   | 60ee331153 | ||
|   | 3a8ce74355 | ||
|   | fd44463238 | ||
|   | 84ba2da553 | ||
|   | e693d96ca1 | ||
|   | 81cb7f8133 | ||
|   | 3471969956 | ||
|   | 7a0985c362 | ||
|   | b79a9e2e67 | ||
|   | 1e037fe6f5 | ||
|   | 0897be57c1 | ||
|   | 4172b06b19 | ||
|   | 32a4875e46 | 
| @@ -5,17 +5,16 @@ set -e | |||||||
| IMAGE_NAME=telecominfraproject/oopt-gnpy | IMAGE_NAME=telecominfraproject/oopt-gnpy | ||||||
| IMAGE_TAG=$(git describe --tags) | IMAGE_TAG=$(git describe --tags) | ||||||
|  |  | ||||||
|  | if [[ "${TRAVIS_BRANCH}" == "experimental/2019-summit" ]]; then | ||||||
|  |   IMAGE_NAME=telecominfraproject/oopt-gnpy-experimental | ||||||
|  | fi | ||||||
|  |  | ||||||
| ALREADY_FOUND=0 | ALREADY_FOUND=0 | ||||||
| docker pull ${IMAGE_NAME}:${IMAGE_TAG} && ALREADY_FOUND=1 | docker pull ${IMAGE_NAME}:${IMAGE_TAG} && ALREADY_FOUND=1 | ||||||
|  |  | ||||||
| if [[ $ALREADY_FOUND == 0 ]]; then | if [[ $ALREADY_FOUND == 0 ]]; then | ||||||
|   docker build . -t ${IMAGE_NAME} |   docker build . -t ${IMAGE_NAME} | ||||||
|   docker tag ${IMAGE_NAME} ${IMAGE_NAME}:${IMAGE_TAG} |   docker tag ${IMAGE_NAME} ${IMAGE_NAME}:${IMAGE_TAG} | ||||||
|  |  | ||||||
|   # shared directory setup: do not clobber the real data |  | ||||||
|   mkdir trash |  | ||||||
|   cd trash |  | ||||||
|   docker run -it --rm --volume $(pwd):/shared ${IMAGE_NAME} ./transmission_main_example.py |  | ||||||
| else | else | ||||||
|   echo "Image ${IMAGE_NAME}:${IMAGE_TAG} already available, will just update the other tags" |   echo "Image ${IMAGE_NAME}:${IMAGE_TAG} already available, will just update the other tags" | ||||||
| fi | fi | ||||||
| @@ -43,5 +42,11 @@ if [[ "${TRAVIS_PULL_REQUEST}" == "false" ]]; then | |||||||
|       docker push ${IMAGE_NAME}:${IMAGE_TAG} |       docker push ${IMAGE_NAME}:${IMAGE_TAG} | ||||||
|     fi |     fi | ||||||
|     docker push ${IMAGE_NAME}:stable |     docker push ${IMAGE_NAME}:stable | ||||||
|  |   elif [[ "${TRAVIS_BRANCH}" == "experimental/2019-summit" ]]; then | ||||||
|  |     echo "Publishing ad-hoc image for the TIP Summit demo" | ||||||
|  |     do_docker_login | ||||||
|  |     if [[ $ALREADY_FOUND == 0 ]]; then | ||||||
|  |       docker push ${IMAGE_NAME}:${IMAGE_TAG} | ||||||
|  |     fi | ||||||
|   fi |   fi | ||||||
| fi | fi | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								.zuul.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.zuul.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | --- | ||||||
|  | - project: | ||||||
|  |     check: | ||||||
|  |       jobs: | ||||||
|  |       - noop | ||||||
|  |     gate: | ||||||
|  |       jobs: | ||||||
|  |       - noop | ||||||
| @@ -2,6 +2,7 @@ FROM python:3.7-slim | |||||||
| COPY . /oopt-gnpy | COPY . /oopt-gnpy | ||||||
| WORKDIR /oopt-gnpy | WORKDIR /oopt-gnpy | ||||||
| RUN python setup.py install | RUN python setup.py install | ||||||
| WORKDIR /shared/examples | WORKDIR /shared | ||||||
| ENTRYPOINT ["/oopt-gnpy/.docker-entry.sh"] | ENTRYPOINT ["/oopt-gnpy/.docker-entry.sh"] | ||||||
| CMD ["/bin/bash"] | CMD ["python", "examples/path_requests_run.py", "examples/2019-demo-topology.json", "examples/2019-demo-services.json", "examples/2019-demo-equipment.json", "--rest"] | ||||||
|  | EXPOSE 5000 | ||||||
|   | |||||||
							
								
								
									
										124
									
								
								examples/2019-demo-equipment.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								examples/2019-demo-equipment.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | |||||||
|  | {     "Edfa":[ | ||||||
|  |  | ||||||
|  |             { | ||||||
|  |             "type_variety": "fixed27", | ||||||
|  |             "type_def": "fixed_gain", | ||||||
|  |             "gain_flatmax": 27, | ||||||
|  |             "gain_min": 27, | ||||||
|  |             "p_max": 21, | ||||||
|  |             "nf0": 5.5, | ||||||
|  |             "allowed_for_design": false | ||||||
|  |             }, | ||||||
|  |  | ||||||
|  |             { | ||||||
|  |             "type_variety": "fixed22", | ||||||
|  |             "type_def": "fixed_gain", | ||||||
|  |             "gain_flatmax": 22, | ||||||
|  |             "gain_min": 22, | ||||||
|  |             "p_max": 21, | ||||||
|  |             "nf0": 5.5, | ||||||
|  |             "allowed_for_design": false | ||||||
|  |             } | ||||||
|  |       ], | ||||||
|  |       "Fiber":[{ | ||||||
|  |             "type_variety": "SSMF", | ||||||
|  |             "dispersion": 1.67e-05, | ||||||
|  |             "gamma": 0.00127 | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |             "type_variety": "NZDF", | ||||||
|  |             "dispersion": 0.5e-05, | ||||||
|  |             "gamma": 0.00146 | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |             "type_variety": "LOF", | ||||||
|  |             "dispersion": 2.2e-05, | ||||||
|  |             "gamma": 0.000843 | ||||||
|  |             } | ||||||
|  |       ], | ||||||
|  |       "Span":[{ | ||||||
|  |             "power_mode": false, | ||||||
|  |             "delta_power_range_db": [-2,3,0.5], | ||||||
|  |             "max_fiber_lineic_loss_for_raman": 0.25, | ||||||
|  |             "target_extended_gain": 2.5, | ||||||
|  |             "max_length": 150, | ||||||
|  |             "length_units": "km", | ||||||
|  |             "max_loss": 28, | ||||||
|  |             "padding": 10, | ||||||
|  |             "EOL": 0, | ||||||
|  |             "con_in": 0, | ||||||
|  |             "con_out": 0 | ||||||
|  |             } | ||||||
|  |       ], | ||||||
|  |       "Roadm":[{ | ||||||
|  |             "target_pch_out_db": -25, | ||||||
|  |             "add_drop_osnr": 30.00, | ||||||
|  |             "restrictions": { | ||||||
|  |                             "preamp_variety_list":[], | ||||||
|  |                             "booster_variety_list":[] | ||||||
|  |                             }             | ||||||
|  |             }], | ||||||
|  |       "SI":[{ | ||||||
|  |             "f_min": 191.6e12, | ||||||
|  |             "baud_rate": 32e9, | ||||||
|  |             "f_max":195.1e12, | ||||||
|  |             "spacing": 50e9, | ||||||
|  |             "power_dbm": 0, | ||||||
|  |             "power_range_db": [0,0,1], | ||||||
|  |             "roll_off": 0.15, | ||||||
|  |             "tx_osnr": 40, | ||||||
|  |             "sys_margins": 2 | ||||||
|  |             }], | ||||||
|  |       "Transceiver":[ | ||||||
|  |             { | ||||||
|  |             "type_variety": "Cassini", | ||||||
|  |             "frequency":{ | ||||||
|  |                         "min": 191.35e12, | ||||||
|  |                         "max": 196.1e12 | ||||||
|  |                         }, | ||||||
|  |             "mode":[ | ||||||
|  |                        { | ||||||
|  |  | ||||||
|  |                        "format": "dp-qpsk", | ||||||
|  |                        "baud_rate": 32e9, | ||||||
|  |                        "OSNR": 11, | ||||||
|  |                        "bit_rate": 100e9, | ||||||
|  |                        "roll_off": 0.15, | ||||||
|  |                        "tx_osnr": 40, | ||||||
|  |                        "min_spacing": 37.5e9, | ||||||
|  |                        "cost":1 | ||||||
|  |                        }, | ||||||
|  |                        { | ||||||
|  |                        "format": "16-qam", | ||||||
|  |                        "baud_rate": 66e9, | ||||||
|  |                        "OSNR": 15, | ||||||
|  |                        "bit_rate": 200e9, | ||||||
|  |                        "roll_off": 0.15, | ||||||
|  |                        "tx_osnr": 40, | ||||||
|  |                        "min_spacing": 75e9, | ||||||
|  |                        "cost":1 | ||||||
|  |                        } | ||||||
|  |                    ] | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |             "type_variety": "Voyager", | ||||||
|  |             "frequency":{ | ||||||
|  |                         "min": 191.35e12, | ||||||
|  |                         "max": 196.1e12 | ||||||
|  |                         }, | ||||||
|  |             "mode":[ | ||||||
|  |                        { | ||||||
|  |                        "format": "mode 1", | ||||||
|  |                        "baud_rate": 32e9, | ||||||
|  |                        "OSNR": 12, | ||||||
|  |                        "bit_rate": 100e9, | ||||||
|  |                        "roll_off": 0.15, | ||||||
|  |                        "tx_osnr": 40, | ||||||
|  |                        "min_spacing": 37.5e9, | ||||||
|  |                        "cost":1 | ||||||
|  |                        } | ||||||
|  |                    ] | ||||||
|  |             } | ||||||
|  |       ] | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										67
									
								
								examples/2019-demo-services.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								examples/2019-demo-services.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | |||||||
|  | { | ||||||
|  |   "path-request": [ | ||||||
|  |     { | ||||||
|  |       "request-id": "first", | ||||||
|  |       "source": "netconf:10.0.254.93:830", | ||||||
|  |       "destination": "netconf:10.0.254.94:830", | ||||||
|  |       "src-tp-id": "trx-Amsterdam", | ||||||
|  |       "dst-tp-id": "trx-Bremen", | ||||||
|  |       "bidirectional": true, | ||||||
|  |       "path-constraints": { | ||||||
|  |         "te-bandwidth": { | ||||||
|  |           "technology": "flexi-grid", | ||||||
|  |           "trx_type": "Cassini", | ||||||
|  |           "trx_mode": null, | ||||||
|  |           "effective-freq-slot": [ | ||||||
|  |             { | ||||||
|  |               "N": "null", | ||||||
|  |               "M": "null" | ||||||
|  |             } | ||||||
|  |           ], | ||||||
|  |           "spacing": 50000000000.0, | ||||||
|  |           "max-nb-of-channel": null, | ||||||
|  |           "output-power": null, | ||||||
|  |           "path_bandwidth": 100000000000.0 | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "request-id": "second", | ||||||
|  |       "source": "netconf:10.0.254.93:830", | ||||||
|  |       "destination": "netconf:10.0.254.94:830", | ||||||
|  |       "src-tp-id": "trx-Amsterdam", | ||||||
|  |       "dst-tp-id": "trx-Bremen", | ||||||
|  |       "bidirectional": true, | ||||||
|  |       "path-constraints": { | ||||||
|  |         "te-bandwidth": { | ||||||
|  |           "technology": "flexi-grid", | ||||||
|  |           "trx_type": "Cassini", | ||||||
|  |           "trx_mode": null, | ||||||
|  |           "effective-freq-slot": [ | ||||||
|  |             { | ||||||
|  |               "N": "null", | ||||||
|  |               "M": "null" | ||||||
|  |             } | ||||||
|  |           ], | ||||||
|  |           "spacing": 50000000000.0, | ||||||
|  |           "max-nb-of-channel": null, | ||||||
|  |           "output-power": null, | ||||||
|  |           "path_bandwidth": 100000000000.0 | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "synchronization": [ | ||||||
|  |     { | ||||||
|  |       "synchronization-id": "some redundancy please", | ||||||
|  |       "svec": { | ||||||
|  |         "relaxable": "false", | ||||||
|  |         "disjointness": "node link", | ||||||
|  |         "request-id-number": [ | ||||||
|  |           "first", | ||||||
|  |           "second" | ||||||
|  |         ] | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   ] | ||||||
|  | } | ||||||
							
								
								
									
										1263
									
								
								examples/2019-demo-topology.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1263
									
								
								examples/2019-demo-topology.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										179
									
								
								examples/2019-generate-tip-demo.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								examples/2019-generate-tip-demo.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | |||||||
|  | # How many nodes in the ring topology? Up to eight is supported, then I ran out of cities.. | ||||||
|  | HOW_MANY = 3 | ||||||
|  |  | ||||||
|  | # city names | ||||||
|  | ALL_CITIES = [ | ||||||
|  |     'Amsterdam', | ||||||
|  |     'Bremen', | ||||||
|  |     'Cologne', | ||||||
|  |     'Dueseldorf', | ||||||
|  |     'Eindhoven', | ||||||
|  |     'Frankfurt', | ||||||
|  |     'Ghent', | ||||||
|  |     'Hague', | ||||||
|  | ] | ||||||
|  | # end of configurable parameters | ||||||
|  |  | ||||||
|  |  | ||||||
|  | J = { | ||||||
|  |     "elements": [], | ||||||
|  |     "connections": [], | ||||||
|  | } | ||||||
|  |  | ||||||
|  | def unidir_join(a, b): | ||||||
|  |     global J | ||||||
|  |     J["connections"].append( | ||||||
|  |         {"from_node": a, "to_node": b} | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  | def mk_edfa(name, gain, voa=0.0): | ||||||
|  |     global J | ||||||
|  |     J["elements"].append( | ||||||
|  |         {"uid": name, "type": "Edfa", "type_variety": f"fixed{gain}", "operational": {"gain_target": gain, "out_voa": voa}} | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  | def add_att(a, b, att): | ||||||
|  |     global J | ||||||
|  |     if att > 0: | ||||||
|  |         uid = f"att-({a})-({b})" | ||||||
|  |     else: | ||||||
|  |         uid = f"splice-({a})-({b})" | ||||||
|  |     J["elements"].append( | ||||||
|  |         {"uid": uid, "type": "Fused", "params": {"loss": att}}, | ||||||
|  |     ) | ||||||
|  |     unidir_join(a, uid) | ||||||
|  |     unidir_join(uid, b) | ||||||
|  |     return uid | ||||||
|  |  | ||||||
|  | def build_fiber(city1, city2): | ||||||
|  |     global J | ||||||
|  |     J["elements"].append( | ||||||
|  |         { | ||||||
|  |             "uid": f"fiber-{city1}-{city2}", | ||||||
|  |             "type": "Fiber", | ||||||
|  |             "type_variety": "SSMF", | ||||||
|  |             "params": { | ||||||
|  |                 "length": 50, | ||||||
|  |                 "length_units": "km", | ||||||
|  |                 "loss_coef": 0.2, | ||||||
|  |                 "con_in": 1.5, | ||||||
|  |                 "con_out": 1.5, | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  | def unidir_patch(a, b): | ||||||
|  |     global J | ||||||
|  |     uid = f"patch-({a})-({b})" | ||||||
|  |     J["elements"].append( | ||||||
|  |         { | ||||||
|  |             "uid": uid, | ||||||
|  |             "type": "Fiber", | ||||||
|  |             "type_variety": "SSMF", | ||||||
|  |             "params": { | ||||||
|  |                 "length": 0, | ||||||
|  |                 "length_units": "km", | ||||||
|  |                 "loss_coef": 0.2, | ||||||
|  |                 "con_in": 0.5, | ||||||
|  |                 "con_out": 0.5, | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     ) | ||||||
|  |     add_att(a, uid, 0.0) | ||||||
|  |     add_att(uid, b, 0.0) | ||||||
|  |  | ||||||
|  | for CITY in (ALL_CITIES[x] for x in range(0, HOW_MANY)): | ||||||
|  |     J["elements"].append( | ||||||
|  |         {"uid": f"trx-{CITY}", "type": "Transceiver"} | ||||||
|  |     ) | ||||||
|  |     target_pwr = [ | ||||||
|  |         {"to_node": f"trx-{CITY}", "target_pch_out_db": -25}, | ||||||
|  |         {"to_node": f"splice-(roadm-{CITY}-AD)-(patch-(roadm-{CITY}-AD)-(roadm-{CITY}-L1))", "target_pch_out_db": -12}, | ||||||
|  |         {"to_node": f"splice-(roadm-{CITY}-AD)-(patch-(roadm-{CITY}-AD)-(roadm-{CITY}-L2))", "target_pch_out_db": -12}, | ||||||
|  |     ] | ||||||
|  |     J["elements"].append( | ||||||
|  |         {"uid": f"roadm-{CITY}-AD", "type": "Roadm", "params": {"target_pch_out_db": -2.0, "per_degree_target_pch_out_db": target_pwr}} | ||||||
|  |     ) | ||||||
|  |     unidir_join(f"trx-{CITY}", f"roadm-{CITY}-AD") | ||||||
|  |     unidir_join(f"roadm-{CITY}-AD", f"trx-{CITY}") | ||||||
|  |  | ||||||
|  |     for n in (1,2): | ||||||
|  |         target_pwr = [ | ||||||
|  |             {"to_node": f"roadm-{CITY}-L{n}-booster", "target_pch_out_db": -23}, | ||||||
|  |             {"to_node": f"splice-(roadm-{CITY}-L{n})-(patch-(roadm-{CITY}-L{n})-(roadm-{CITY}-AD))", "target_pch_out_db": -12}, | ||||||
|  |         ] | ||||||
|  |         for m in (1,2): | ||||||
|  |             if m == n: | ||||||
|  |                 continue | ||||||
|  |             target_pwr.append( | ||||||
|  |               {"to_node": f"splice-(roadm-{CITY}-L{n})-(patch-(roadm-{CITY}-L{n})-(roadm-{CITY}-L{m}))", "target_pch_out_db": -12}, | ||||||
|  |             ) | ||||||
|  |         J["elements"].append( | ||||||
|  |             {"uid": f"roadm-{CITY}-L{n}", "type": "Roadm", "params": {"target_pch_out_db": -23.0, "per_degree_target_pch_out_db": target_pwr}} | ||||||
|  |         ) | ||||||
|  |         mk_edfa(f"roadm-{CITY}-L{n}-booster", 22) | ||||||
|  |         mk_edfa(f"roadm-{CITY}-L{n}-preamp", 27) | ||||||
|  |         unidir_join(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-L{n}-booster") | ||||||
|  |         unidir_join(f"roadm-{CITY}-L{n}-preamp", f"roadm-{CITY}-L{n}") | ||||||
|  |  | ||||||
|  |         unidir_patch(f"roadm-{CITY}-AD", f"roadm-{CITY}-L{n}") | ||||||
|  |         unidir_patch(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-AD") | ||||||
|  |         for m in (1,2): | ||||||
|  |             if m == n: | ||||||
|  |                 continue | ||||||
|  |             #add_att(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-L{m}", 22) | ||||||
|  |             unidir_patch(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-L{m}") | ||||||
|  |  | ||||||
|  | for city1, city2 in ((ALL_CITIES[i], ALL_CITIES[i + 1] if i < HOW_MANY - 1 else ALL_CITIES[0]) for i in range(0, HOW_MANY)): | ||||||
|  |     build_fiber(city1, city2) | ||||||
|  |     unidir_join(f"roadm-{city1}-L1-booster", f"fiber-{city1}-{city2}") | ||||||
|  |     unidir_join(f"fiber-{city1}-{city2}", f"roadm-{city2}-L2-preamp") | ||||||
|  |     build_fiber(city2, city1) | ||||||
|  |     unidir_join(f"roadm-{city2}-L2-booster", f"fiber-{city2}-{city1}") | ||||||
|  |     unidir_join(f"fiber-{city2}-{city1}", f"roadm-{city1}-L1-preamp") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | for _, E in enumerate(J["elements"]): | ||||||
|  |     uid = E["uid"] | ||||||
|  |     if uid.startswith("roadm-") and (uid.endswith("-L1-booster") or uid.endswith("-L2-booster")): | ||||||
|  |         E["operational"]["out_voa"] = 12.0 | ||||||
|  |     #if uid.endswith("-AD-add"): | ||||||
|  |     #    E["operational"]["out_voa"] = 21 | ||||||
|  |  | ||||||
|  | translate = { | ||||||
|  |     #"trx-Amsterdam": "10.0.254.93", | ||||||
|  |     #"trx-Bremen": "10.0.254.94", | ||||||
|  |     "trx-Amsterdam": "10.0.254.76", | ||||||
|  |     "trx-Bremen": "10.0.254.77", | ||||||
|  |  | ||||||
|  |     # Amsterdam A/D: coherent-v9u | ||||||
|  |     "roadm-Amsterdam-AD": "10.0.254.107", | ||||||
|  |     # Bremen A/D: -spi | ||||||
|  |     "roadm-Bremen-AD": "10.0.254.225", | ||||||
|  |  | ||||||
|  |     # Amsterdam -> Bremen ...QR79 | ||||||
|  |     "roadm-Amsterdam-L1": "10.0.254.78", | ||||||
|  |     # Bremen -> Amsterdam ...QCP9 | ||||||
|  |     "roadm-Bremen-L2": "10.0.254.102", | ||||||
|  |  | ||||||
|  |     # Bremen -> Cologne ...WKP | ||||||
|  |     "roadm-Bremen-L1": "10.0.254.100", | ||||||
|  |     # Cologne -> Bremen ...QLK6 | ||||||
|  |     "roadm-Cologne-L2": "10.0.254.104", | ||||||
|  |  | ||||||
|  |     # Cologne -> Amsterdam ...TQQ | ||||||
|  |     "roadm-Cologne-L1": "10.0.254.99", | ||||||
|  |     # Amsterdam -> Cologne ...Q7JS | ||||||
|  |     "roadm-Amsterdam-L2": "10.0.254.79", | ||||||
|  |  | ||||||
|  |     # spare Line/Degree ...QC8B | ||||||
|  |     "spare-line-degree": "10.0.254.101", | ||||||
|  |     # spare Add/Drop: ...NNN | ||||||
|  |     "spare-add-drop": "10.0.254.228", | ||||||
|  | } | ||||||
|  |  | ||||||
|  | import json | ||||||
|  | s = json.dumps(J, indent=2) | ||||||
|  | for (old, new) in translate.items(): | ||||||
|  |     s = s.replace(f'"{old}"', f'"netconf:{new}:830"') | ||||||
|  | print(s) | ||||||
							
								
								
									
										1033
									
								
								examples/demo.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1033
									
								
								examples/demo.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -21,7 +21,7 @@ from json import dumps, loads | |||||||
| from numpy import mean | from numpy import mean | ||||||
| from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element | from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element | ||||||
| from gnpy.core.utils import load_json | from gnpy.core.utils import load_json | ||||||
| from gnpy.core.network import load_network, build_network, save_network | from gnpy.core.network import load_network, build_network, save_network, network_from_json | ||||||
| from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch | from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch | ||||||
| from gnpy.core.elements import Transceiver, Roadm | from gnpy.core.elements import Transceiver, Roadm | ||||||
| from gnpy.core.utils import db2lin, lin2db | from gnpy.core.utils import db2lin, lin2db | ||||||
| @@ -38,6 +38,9 @@ from copy import copy, deepcopy | |||||||
| from textwrap import dedent | from textwrap import dedent | ||||||
| from math import ceil | from math import ceil | ||||||
|  |  | ||||||
|  | from flask import Flask, jsonify, make_response, request | ||||||
|  | from flask_restful import Api, Resource, reqparse, fields | ||||||
|  |  | ||||||
| #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' | #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' | ||||||
|  |  | ||||||
| LOGGER = getLogger(__name__) | LOGGER = getLogger(__name__) | ||||||
| @@ -58,7 +61,12 @@ PARSER.add_argument('-bi', '--bidir', action='store_true',\ | |||||||
| PARSER.add_argument('-v', '--verbose', action='count', default=0,\ | PARSER.add_argument('-v', '--verbose', action='count', default=0,\ | ||||||
|                     help='increases verbosity for each occurence') |                     help='increases verbosity for each occurence') | ||||||
| PARSER.add_argument('-o', '--output', type=Path) | PARSER.add_argument('-o', '--output', type=Path) | ||||||
|  | PARSER.add_argument('-r', '--rest', action='count', default=0, help='use the REST API') | ||||||
|  |  | ||||||
|  | NETWORK_FILENAME = 'topoDemov1.json' #'disagregatedTopoDemov1.json' # | ||||||
|  |  | ||||||
|  | APP = Flask(__name__, static_url_path="") | ||||||
|  | API = Api(APP) | ||||||
|  |  | ||||||
| def requests_from_json(json_data, equipment): | def requests_from_json(json_data, equipment): | ||||||
|     """ converts the json data into a list of requests elements |     """ converts the json data into a list of requests elements | ||||||
| @@ -360,32 +368,9 @@ def path_result_json(pathresult): | |||||||
|     } |     } | ||||||
|     return data |     return data | ||||||
|  |  | ||||||
| def main(args): | def compute_requests(network, data, equipment): | ||||||
|     """ main function that calls all functions |     """ Main program calling functions | ||||||
|     """ |     """ | ||||||
|     LOGGER.info(f'Computing path requests {args.service_filename} into JSON format') |  | ||||||
|     print('\x1b[1;34;40m' +\ |  | ||||||
|           f'Computing path requests {args.service_filename} into JSON format'+ '\x1b[0m') |  | ||||||
|     # for debug |  | ||||||
|     # print( args.eqpt_filename) |  | ||||||
|  |  | ||||||
|     try: |  | ||||||
|         data = load_requests(args.service_filename, args.eqpt_filename, args.bidir) |  | ||||||
|         equipment = load_equipment(args.eqpt_filename) |  | ||||||
|         network = load_network(args.network_filename, equipment) |  | ||||||
|     except EquipmentConfigError as this_e: |  | ||||||
|         print(f'{ansi_escapes.red}Configuration error in the equipment library:{ansi_escapes.reset} {this_e}') |  | ||||||
|         exit(1) |  | ||||||
|     except NetworkTopologyError as this_e: |  | ||||||
|         print(f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {this_e}') |  | ||||||
|         exit(1) |  | ||||||
|     except ConfigurationError as this_e: |  | ||||||
|         print(f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {this_e}') |  | ||||||
|         exit(1) |  | ||||||
|     except ServiceError as this_e: |  | ||||||
|         print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}') |  | ||||||
|         exit(1) |  | ||||||
|  |  | ||||||
|     # Build the network once using the default power defined in SI in eqpt config |     # Build the network once using the default power defined in SI in eqpt config | ||||||
|     # TODO power density: db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by |     # TODO power density: db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by | ||||||
|     # spacing, f_min and f_max |     # spacing, f_min and f_max | ||||||
| @@ -394,7 +379,7 @@ def main(args): | |||||||
|     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ |     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) |         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||||
|     build_network(network, equipment, p_db, p_total_db) |     build_network(network, equipment, p_db, p_total_db) | ||||||
|     save_network(args.network_filename, network) |     save_network(ARGS.network_filename, network) | ||||||
|  |  | ||||||
|     oms_list = build_oms_list(network, equipment) |     oms_list = build_oms_list(network, equipment) | ||||||
|  |  | ||||||
| @@ -402,7 +387,7 @@ def main(args): | |||||||
|         rqs = requests_from_json(data, equipment) |         rqs = requests_from_json(data, equipment) | ||||||
|     except ServiceError as this_e: |     except ServiceError as this_e: | ||||||
|         print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}') |         print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}') | ||||||
|         exit(1) |         raise this_e | ||||||
|     # check that request ids are unique. Non unique ids, may |     # check that request ids are unique. Non unique ids, may | ||||||
|     # mess the computation: better to stop the computation |     # mess the computation: better to stop the computation | ||||||
|     all_ids = [r.request_id for r in rqs] |     all_ids = [r.request_id for r in rqs] | ||||||
| @@ -411,12 +396,13 @@ def main(args): | |||||||
|             all_ids.remove(item) |             all_ids.remove(item) | ||||||
|         msg = f'Requests id {all_ids} are not unique' |         msg = f'Requests id {all_ids} are not unique' | ||||||
|         LOGGER.critical(msg) |         LOGGER.critical(msg) | ||||||
|         exit() |         raise ServiceError(msg) | ||||||
|     try: |     try: | ||||||
|         rqs = correct_route_list(network, rqs) |         rqs = correct_route_list(network, rqs) | ||||||
|     except ServiceError as this_e: |     except ServiceError as this_e: | ||||||
|         print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}') |         print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}') | ||||||
|         exit(1) |         raise this_e | ||||||
|  |         #exit(1) | ||||||
|     # pths = compute_path(network, equipment, rqs) |     # pths = compute_path(network, equipment, rqs) | ||||||
|     dsjn = disjunctions_from_json(data) |     dsjn = disjunctions_from_json(data) | ||||||
|  |  | ||||||
| @@ -440,7 +426,7 @@ def main(args): | |||||||
|         pths = compute_path_dsjctn(network, equipment, rqs, dsjn) |         pths = compute_path_dsjctn(network, equipment, rqs, dsjn) | ||||||
|     except DisjunctionError as this_e: |     except DisjunctionError as this_e: | ||||||
|         print(f'{ansi_escapes.red}Disjunction error:{ansi_escapes.reset} {this_e}') |         print(f'{ansi_escapes.red}Disjunction error:{ansi_escapes.reset} {this_e}') | ||||||
|         exit(1) |         raise this_e | ||||||
|  |  | ||||||
|     print('\x1b[1;34;40m' + f'Propagating on selected path' + '\x1b[0m') |     print('\x1b[1;34;40m' + f'Propagating on selected path' + '\x1b[0m') | ||||||
|     propagatedpths, reversed_pths, reversed_propagatedpths = \ |     propagatedpths, reversed_pths, reversed_propagatedpths = \ | ||||||
| @@ -493,20 +479,89 @@ def main(args): | |||||||
|     print('\x1b[1;33;40m'+f'Result summary shows mean SNR and OSNR (average over all channels)' +\ |     print('\x1b[1;33;40m'+f'Result summary shows mean SNR and OSNR (average over all channels)' +\ | ||||||
|           '\x1b[0m') |           '\x1b[0m') | ||||||
|  |  | ||||||
|     if args.output: |     return propagatedpths, reversed_propagatedpths, rqs | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def launch_cli(network, data, equipment): | ||||||
|  |     """ Compute requests using network, data and equipment with client line interface | ||||||
|  |     """ | ||||||
|  |     propagatedpths, reversed_propagatedpths, rqs = compute_requests(network, data, equipment) | ||||||
|  |     #Generate the output | ||||||
|  |     if ARGS.output : | ||||||
|         result = [] |         result = [] | ||||||
|         # assumes that list of rqs and list of propgatedpths have same order |         # assumes that list of rqs and list of propgatedpths have same order | ||||||
|         for i, pth in enumerate(propagatedpths): |         for i, pth in enumerate(propagatedpths): | ||||||
|             result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i])) |             result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i])) | ||||||
|         temp = path_result_json(result) |         temp = path_result_json(result) | ||||||
|         fnamecsv = f'{str(args.output)[0:len(str(args.output))-len(str(args.output.suffix))]}.csv' |         fnamecsv = f'{str(ARGS.output)[0:len(str(ARGS.output))-len(str(ARGS.output.suffix))]}.csv' | ||||||
|         fnamejson = f'{str(args.output)[0:len(str(args.output))-len(str(args.output.suffix))]}.json' |         fnamejson = f'{str(ARGS.output)[0:len(str(ARGS.output))-len(str(ARGS.output.suffix))]}.json' | ||||||
|         with open(fnamejson, 'w', encoding='utf-8') as fjson: |         with open(fnamejson, 'w', encoding='utf-8') as fjson: | ||||||
|             fjson.write(dumps(path_result_json(result), indent=2, ensure_ascii=False)) |             fjson.write(dumps(path_result_json(result), indent=2, ensure_ascii=False)) | ||||||
|             with open(fnamecsv, "w", encoding='utf-8') as fcsv: |             with open(fnamecsv, "w", encoding='utf-8') as fcsv: | ||||||
|                 jsontocsv(temp, equipment, fcsv) |                 jsontocsv(temp, equipment, fcsv) | ||||||
|                 print('\x1b[1;34;40m'+f'saving in {args.output} and {fnamecsv}'+ '\x1b[0m') |                 print('\x1b[1;34;40m'+f'saving in {ARGS.output} and {fnamecsv}'+ '\x1b[0m') | ||||||
|  |  | ||||||
|  | class GnpyAPI(Resource): | ||||||
|  |     """ Compute requests using network, data and equipment with rest api | ||||||
|  |     """ | ||||||
|  |     def get(self): | ||||||
|  |         return {"ping": True}, 200 | ||||||
|  |  | ||||||
|  |     def post(self): | ||||||
|  |         data = request.get_json() | ||||||
|  |         equipment = load_equipment('examples/2019-demo-equipment.json') | ||||||
|  |         topo_json = load_json('examples/2019-demo-topology.json') | ||||||
|  |         network = network_from_json(topo_json, equipment) | ||||||
|  |         try: | ||||||
|  |             propagatedpths, reversed_propagatedpths, rqs = compute_requests(network, data, equipment) | ||||||
|  |             # Generate the output | ||||||
|  |             result = [] | ||||||
|  |             #assumes that list of rqs and list of propgatedpths have same order | ||||||
|  |             for i, pth in enumerate(propagatedpths): | ||||||
|  |                 result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i])) | ||||||
|  |  | ||||||
|  |             return {"result":path_result_json(result)}, 201 | ||||||
|  |         except ServiceError as this_e: | ||||||
|  |             msg = f'Service error: {this_e}' | ||||||
|  |             return {"result": msg}, 400 | ||||||
|  |  | ||||||
|  | API.add_resource(GnpyAPI, '/gnpy-experimental') | ||||||
|  |  | ||||||
|  | def main(args): | ||||||
|  |     """ main function that calls all functions | ||||||
|  |     """ | ||||||
|  |     LOGGER.info(f'Computing path requests {args.service_filename} into JSON format') | ||||||
|  |     print('\x1b[1;34;40m' +\ | ||||||
|  |           f'Computing path requests {args.service_filename} into JSON format'+ '\x1b[0m') | ||||||
|  |     # for debug | ||||||
|  |     # print( args.eqpt_filename) | ||||||
|  |  | ||||||
|  |     try: | ||||||
|  |         data = load_requests(args.service_filename, args.eqpt_filename, args.bidir) | ||||||
|  |         equipment = load_equipment(args.eqpt_filename) | ||||||
|  |         network = load_network(args.network_filename, equipment) | ||||||
|  |     except EquipmentConfigError as this_e: | ||||||
|  |         print(f'{ansi_escapes.red}Configuration error in the equipment library:{ansi_escapes.reset} {this_e}') | ||||||
|  |         exit(1) | ||||||
|  |     except NetworkTopologyError as this_e: | ||||||
|  |         print(f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {this_e}') | ||||||
|  |         exit(1) | ||||||
|  |     except ConfigurationError as this_e: | ||||||
|  |         print(f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {this_e}') | ||||||
|  |         exit(1) | ||||||
|  |     except ServiceError as this_e: | ||||||
|  |         print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}') | ||||||
|  |         exit(1) | ||||||
|  |     # input_str = raw_input("How will you use your program: c:[cli] , a:[api] ?") | ||||||
|  |     # print(input_str) | ||||||
|  |     # | ||||||
|  |     if ((args.rest == 1) and (args.output is None)): | ||||||
|  |         print('you have chosen the rest mode') | ||||||
|  |         APP.run(host='0.0.0.0', port=5000, debug=True) | ||||||
|  |     elif ((args.rest > 1) or ((args.rest == 1) and (args.output is not None))): | ||||||
|  |         print('command is not well formulated') | ||||||
|  |     else: | ||||||
|  |         launch_cli(network, data, equipment) | ||||||
|  |  | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     ARGS = PARSER.parse_args() |     ARGS = PARSER.parse_args() | ||||||
|   | |||||||
							
								
								
									
										180
									
								
								examples/serviceDemov1.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								examples/serviceDemov1.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,180 @@ | |||||||
|  | { | ||||||
|  |   "path-request": [ | ||||||
|  |     { | ||||||
|  |       "request-id": "0", | ||||||
|  |       "source": "trx site_a", | ||||||
|  |       "destination": "trx site_b", | ||||||
|  |       "src-tp-id": "trx site_a", | ||||||
|  |       "dst-tp-id": "trx site_b", | ||||||
|  |       "bidirectional": false, | ||||||
|  |       "path-constraints": { | ||||||
|  |         "te-bandwidth": { | ||||||
|  |           "technology": "flexi-grid", | ||||||
|  |           "trx_type": "Voyager", | ||||||
|  |           "trx_mode": null, | ||||||
|  |           "effective-freq-slot": [ | ||||||
|  |             { | ||||||
|  |               "N": "null", | ||||||
|  |               "M": "null" | ||||||
|  |             } | ||||||
|  |           ], | ||||||
|  |           "spacing": 50000000000.0, | ||||||
|  |           "max-nb-of-channel": null, | ||||||
|  |           "output-power": null, | ||||||
|  |           "path_bandwidth": 100000000000.0 | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "request-id": "1", | ||||||
|  |       "source": "trx site_a", | ||||||
|  |       "destination": "trx site_b", | ||||||
|  |       "src-tp-id": "trx site_a", | ||||||
|  |       "dst-tp-id": "trx site_b", | ||||||
|  |       "bidirectional": false, | ||||||
|  |       "path-constraints": { | ||||||
|  |         "te-bandwidth": { | ||||||
|  |           "technology": "flexi-grid", | ||||||
|  |           "trx_type": "Voyager", | ||||||
|  |           "trx_mode": "mode 1", | ||||||
|  |           "effective-freq-slot": [ | ||||||
|  |             { | ||||||
|  |               "N": "null", | ||||||
|  |               "M": "null" | ||||||
|  |             } | ||||||
|  |           ], | ||||||
|  |           "spacing": 50000000000.0, | ||||||
|  |           "max-nb-of-channel": null, | ||||||
|  |           "output-power": null, | ||||||
|  |           "path_bandwidth": 200000000000.0 | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       "explicit-route-objects": { | ||||||
|  |         "route-object-include-exclude": [ | ||||||
|  |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|  |             "index": 0, | ||||||
|  |             "num-unnum-hop": { | ||||||
|  |               "node-id": "Span1ab", | ||||||
|  |               "link-tp-id": "link-tp-id is not used", | ||||||
|  |               "hop-type": "STRICT" | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         ] | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "request-id": "2", | ||||||
|  |       "source": "trx site_a", | ||||||
|  |       "destination": "trx site_b", | ||||||
|  |       "src-tp-id": "trx site_a", | ||||||
|  |       "dst-tp-id": "trx site_b", | ||||||
|  |       "bidirectional": false, | ||||||
|  |       "path-constraints": { | ||||||
|  |         "te-bandwidth": { | ||||||
|  |           "technology": "flexi-grid", | ||||||
|  |           "trx_type": "Voyager", | ||||||
|  |           "trx_mode": "mode 1", | ||||||
|  |           "effective-freq-slot": [ | ||||||
|  |             { | ||||||
|  |               "N": "null", | ||||||
|  |               "M": "null" | ||||||
|  |             } | ||||||
|  |           ], | ||||||
|  |           "spacing": 50000000000.0, | ||||||
|  |           "max-nb-of-channel": null, | ||||||
|  |           "output-power": null, | ||||||
|  |           "path_bandwidth": 200000000000.0 | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       "explicit-route-objects": { | ||||||
|  |         "route-object-include-exclude": [ | ||||||
|  |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|  |             "index": 0, | ||||||
|  |             "num-unnum-hop": { | ||||||
|  |               "node-id": "roadm site_c", | ||||||
|  |               "link-tp-id": "link-tp-id is not used", | ||||||
|  |               "hop-type": "STRICT" | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         ] | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "request-id": "3", | ||||||
|  |       "source": "trx site_a", | ||||||
|  |       "destination": "trx site_b", | ||||||
|  |       "src-tp-id": "trx site_a", | ||||||
|  |       "dst-tp-id": "trx site_b", | ||||||
|  |       "bidirectional": false, | ||||||
|  |       "path-constraints": { | ||||||
|  |         "te-bandwidth": { | ||||||
|  |           "technology": "flexi-grid", | ||||||
|  |           "trx_type": "Voyager", | ||||||
|  |           "trx_mode": null, | ||||||
|  |           "effective-freq-slot": [ | ||||||
|  |             { | ||||||
|  |               "N": "null", | ||||||
|  |               "M": "null" | ||||||
|  |             } | ||||||
|  |           ], | ||||||
|  |           "spacing": 50000000000.0, | ||||||
|  |           "max-nb-of-channel": null, | ||||||
|  |           "output-power": null, | ||||||
|  |           "path_bandwidth": 100000000000.0 | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "request-id": "4", | ||||||
|  |       "source": "trx site_a", | ||||||
|  |       "destination": "trx site_b", | ||||||
|  |       "src-tp-id": "trx site_a", | ||||||
|  |       "dst-tp-id": "trx site_b", | ||||||
|  |       "bidirectional": false, | ||||||
|  |       "path-constraints": { | ||||||
|  |         "te-bandwidth": { | ||||||
|  |           "technology": "flexi-grid", | ||||||
|  |           "trx_type": "Voyager", | ||||||
|  |           "trx_mode": null, | ||||||
|  |           "effective-freq-slot": [ | ||||||
|  |             { | ||||||
|  |               "N": "null", | ||||||
|  |               "M": "null" | ||||||
|  |             } | ||||||
|  |           ], | ||||||
|  |           "spacing": 50000000000.0, | ||||||
|  |           "max-nb-of-channel": null, | ||||||
|  |           "output-power": null, | ||||||
|  |           "path_bandwidth": 100000000000.0 | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "synchronization": [ | ||||||
|  |     { | ||||||
|  |       "synchronization-id": "x", | ||||||
|  |       "svec": { | ||||||
|  |         "relaxable": "false", | ||||||
|  |         "disjointness": "node link", | ||||||
|  |         "request-id-number": [ | ||||||
|  |           "3", | ||||||
|  |           "0" | ||||||
|  |         ] | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "synchronization-id": "y", | ||||||
|  |       "svec": { | ||||||
|  |         "relaxable": "false", | ||||||
|  |         "disjointness": "node link", | ||||||
|  |         "request-id-number": [ | ||||||
|  |           "4", | ||||||
|  |           "3", | ||||||
|  |           "0" | ||||||
|  |         ] | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   ] | ||||||
|  | } | ||||||
							
								
								
									
										703
									
								
								examples/topoDemov1.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										703
									
								
								examples/topoDemov1.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,703 @@ | |||||||
|  | { | ||||||
|  |   "elements": [ | ||||||
|  |     { | ||||||
|  |       "uid": "trx site_a", | ||||||
|  |       "type": "Transceiver", | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0, | ||||||
|  |           "longitude": 0, | ||||||
|  |           "city": "Site a", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "roadm site_a", | ||||||
|  |       "type": "Roadm", | ||||||
|  |       "params": { | ||||||
|  |         "target_pch_out_db": -20, | ||||||
|  |         "restrictions": { | ||||||
|  |           "preamp_variety_list": [], | ||||||
|  |           "booster_variety_list": [] | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0, | ||||||
|  |           "longitude": 0, | ||||||
|  |           "city": "Site a", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "Span1ab", | ||||||
|  |       "type": "Fiber", | ||||||
|  |       "type_variety": "SSMF", | ||||||
|  |       "params": { | ||||||
|  |         "type_variety": "SSMF", | ||||||
|  |         "length": 100.0, | ||||||
|  |         "loss_coef": 0.2, | ||||||
|  |         "length_units": "km", | ||||||
|  |         "att_in": 0, | ||||||
|  |         "con_in": 0.5, | ||||||
|  |         "con_out": 0.5 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 1, | ||||||
|  |           "longitude": 0, | ||||||
|  |           "city": null, | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "Span1ba", | ||||||
|  |       "type": "Fiber", | ||||||
|  |       "type_variety": "SSMF", | ||||||
|  |       "params": { | ||||||
|  |         "type_variety": "SSMF", | ||||||
|  |         "length": 100.0, | ||||||
|  |         "loss_coef": 0.2, | ||||||
|  |         "length_units": "km", | ||||||
|  |         "att_in": 0, | ||||||
|  |         "con_in": 0.5, | ||||||
|  |         "con_out": 0.5 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 1, | ||||||
|  |           "longitude": 0, | ||||||
|  |           "city": null, | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "Span2ab", | ||||||
|  |       "type": "Fiber", | ||||||
|  |       "type_variety": "SSMF", | ||||||
|  |       "params": { | ||||||
|  |         "type_variety": "SSMF", | ||||||
|  |         "length": 80.0, | ||||||
|  |         "loss_coef": 0.2, | ||||||
|  |         "length_units": "km", | ||||||
|  |         "att_in": 0, | ||||||
|  |         "con_in": 0.5, | ||||||
|  |         "con_out": 0.5 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 1, | ||||||
|  |           "longitude": 0, | ||||||
|  |           "city": null, | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "Span2ba", | ||||||
|  |       "type": "Fiber", | ||||||
|  |       "type_variety": "SSMF", | ||||||
|  |       "params": { | ||||||
|  |         "type_variety": "SSMF", | ||||||
|  |         "length": 80.0, | ||||||
|  |         "loss_coef": 0.2, | ||||||
|  |         "length_units": "km", | ||||||
|  |         "att_in": 0, | ||||||
|  |         "con_in": 0.5, | ||||||
|  |         "con_out": 0.5 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 1, | ||||||
|  |           "longitude": 0, | ||||||
|  |           "city": null, | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "roadm site_b", | ||||||
|  |       "type": "Roadm", | ||||||
|  |       "params": { | ||||||
|  |         "target_pch_out_db": -20, | ||||||
|  |         "restrictions": { | ||||||
|  |           "preamp_variety_list": [], | ||||||
|  |           "booster_variety_list": [] | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0, | ||||||
|  |           "longitude": 0, | ||||||
|  |           "city": "Site b", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "trx site_b", | ||||||
|  |       "type": "Transceiver", | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 2, | ||||||
|  |           "longitude": 0, | ||||||
|  |           "city": "Site b", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "booster1 site_a", | ||||||
|  |       "type": "Edfa", | ||||||
|  |       "type_variety": "std_medium_gain", | ||||||
|  |       "operational": { | ||||||
|  |         "gain_target": 19.0, | ||||||
|  |         "delta_p": -1.0, | ||||||
|  |         "tilt_target": 0, | ||||||
|  |         "out_voa": 0 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0.5, | ||||||
|  |           "longitude": 0.0, | ||||||
|  |           "city": "Site a", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "preamp site_b", | ||||||
|  |       "type": "Edfa", | ||||||
|  |       "type_variety": "std_low_gain", | ||||||
|  |       "operational": { | ||||||
|  |         "gain_target": 18.0, | ||||||
|  |         "delta_p": 0, | ||||||
|  |         "tilt_target": 0, | ||||||
|  |         "out_voa": 0 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0.5, | ||||||
|  |           "longitude": 0.0, | ||||||
|  |           "city": "Site b", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "booster1 site_b", | ||||||
|  |       "type": "Edfa", | ||||||
|  |       "type_variety": "std_medium_gain", | ||||||
|  |       "operational": { | ||||||
|  |         "gain_target": 19.0, | ||||||
|  |         "delta_p": -1.0, | ||||||
|  |         "tilt_target": 0, | ||||||
|  |         "out_voa": 0 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0.5, | ||||||
|  |           "longitude": 0.0, | ||||||
|  |           "city": "Site b", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "preamp1 site_a", | ||||||
|  |       "type": "Edfa", | ||||||
|  |       "type_variety": "std_low_gain", | ||||||
|  |       "operational": { | ||||||
|  |         "gain_target": 18.0, | ||||||
|  |         "delta_p": 0, | ||||||
|  |         "tilt_target": 0, | ||||||
|  |         "out_voa": 0 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0.5, | ||||||
|  |           "longitude": 0.0, | ||||||
|  |           "city": "Site_a", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "booster2 site_a", | ||||||
|  |       "type": "Edfa", | ||||||
|  |       "type_variety": "std_medium_gain", | ||||||
|  |       "operational": { | ||||||
|  |         "gain_target": 19.0, | ||||||
|  |         "delta_p": -1.0, | ||||||
|  |         "tilt_target": 0, | ||||||
|  |         "out_voa": 0 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0.5, | ||||||
|  |           "longitude": 0.0, | ||||||
|  |           "city": "Site a", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "preamp2 site_b", | ||||||
|  |       "type": "Edfa", | ||||||
|  |       "type_variety": "std_low_gain", | ||||||
|  |       "operational": { | ||||||
|  |         "gain_target": 18.0, | ||||||
|  |         "delta_p": 0, | ||||||
|  |         "tilt_target": 0, | ||||||
|  |         "out_voa": 0 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0.5, | ||||||
|  |           "longitude": 0.0, | ||||||
|  |           "city": "Site_b", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "booster2 site_b", | ||||||
|  |       "type": "Edfa", | ||||||
|  |       "type_variety": "std_medium_gain", | ||||||
|  |       "operational": { | ||||||
|  |         "gain_target": 19.0, | ||||||
|  |         "delta_p": -1.0, | ||||||
|  |         "tilt_target": 0, | ||||||
|  |         "out_voa": 0 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0.5, | ||||||
|  |           "longitude": 0.0, | ||||||
|  |           "city": "Site b", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "preamp2 site_a", | ||||||
|  |       "type": "Edfa", | ||||||
|  |       "type_variety": "std_low_gain", | ||||||
|  |       "operational": { | ||||||
|  |         "gain_target": 18.0, | ||||||
|  |         "delta_p": 0, | ||||||
|  |         "tilt_target": 0, | ||||||
|  |         "out_voa": 0 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0.5, | ||||||
|  |           "longitude": 0.0, | ||||||
|  |           "city": "Site_a", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "booster3 site_a", | ||||||
|  |       "type": "Edfa", | ||||||
|  |       "type_variety": "std_medium_gain", | ||||||
|  |       "operational": { | ||||||
|  |         "gain_target": 19.0, | ||||||
|  |         "delta_p": -1.0, | ||||||
|  |         "tilt_target": 0, | ||||||
|  |         "out_voa": 0 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0.5, | ||||||
|  |           "longitude": 0.0, | ||||||
|  |           "city": "Site a", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "preamp3 site_b", | ||||||
|  |       "type": "Edfa", | ||||||
|  |       "type_variety": "std_low_gain", | ||||||
|  |       "operational": { | ||||||
|  |         "gain_target": 18.0, | ||||||
|  |         "delta_p": 0, | ||||||
|  |         "tilt_target": 0, | ||||||
|  |         "out_voa": 0 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0.5, | ||||||
|  |           "longitude": 0.0, | ||||||
|  |           "city": "Site_b", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "booster3 site_b", | ||||||
|  |       "type": "Edfa", | ||||||
|  |       "type_variety": "std_medium_gain", | ||||||
|  |       "operational": { | ||||||
|  |         "gain_target": 19.0, | ||||||
|  |         "delta_p": -1.0, | ||||||
|  |         "tilt_target": 0, | ||||||
|  |         "out_voa": 0 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0.5, | ||||||
|  |           "longitude": 0.0, | ||||||
|  |           "city": "Site b", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "preamp3 site_a", | ||||||
|  |       "type": "Edfa", | ||||||
|  |       "type_variety": "std_low_gain", | ||||||
|  |       "operational": { | ||||||
|  |         "gain_target": 18.0, | ||||||
|  |         "delta_p": 0, | ||||||
|  |         "tilt_target": 0, | ||||||
|  |         "out_voa": 0 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0.5, | ||||||
|  |           "longitude": 0.0, | ||||||
|  |           "city": "Site_a", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "roadm site_c", | ||||||
|  |       "type": "Roadm", | ||||||
|  |       "params": { | ||||||
|  |         "target_pch_out_db": -20, | ||||||
|  |         "restrictions": { | ||||||
|  |           "preamp_variety_list": [], | ||||||
|  |           "booster_variety_list": [] | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0, | ||||||
|  |           "longitude": 0, | ||||||
|  |           "city": "Site c", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "booster1 site_c", | ||||||
|  |       "type": "Edfa", | ||||||
|  |       "type_variety": "std_medium_gain", | ||||||
|  |       "operational": { | ||||||
|  |         "gain_target": 19.0, | ||||||
|  |         "delta_p": -1.0, | ||||||
|  |         "tilt_target": 0, | ||||||
|  |         "out_voa": 0 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0.5, | ||||||
|  |           "longitude": 0.0, | ||||||
|  |           "city": "Site c", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "preamp1 site_c", | ||||||
|  |       "type": "Edfa", | ||||||
|  |       "type_variety": "std_low_gain", | ||||||
|  |       "operational": { | ||||||
|  |         "gain_target": 18.0, | ||||||
|  |         "delta_p": 0, | ||||||
|  |         "tilt_target": 0, | ||||||
|  |         "out_voa": 0 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0.5, | ||||||
|  |           "longitude": 0.0, | ||||||
|  |           "city": "Site_c", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "booster2 site_c", | ||||||
|  |       "type": "Edfa", | ||||||
|  |       "type_variety": "std_medium_gain", | ||||||
|  |       "operational": { | ||||||
|  |         "gain_target": 19.0, | ||||||
|  |         "delta_p": -1.0, | ||||||
|  |         "tilt_target": 0, | ||||||
|  |         "out_voa": 0 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0.5, | ||||||
|  |           "longitude": 0.0, | ||||||
|  |           "city": "Site c", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "preamp2 site_c", | ||||||
|  |       "type": "Edfa", | ||||||
|  |       "type_variety": "std_low_gain", | ||||||
|  |       "operational": { | ||||||
|  |         "gain_target": 18.0, | ||||||
|  |         "delta_p": 0, | ||||||
|  |         "tilt_target": 0, | ||||||
|  |         "out_voa": 0 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 0.5, | ||||||
|  |           "longitude": 0.0, | ||||||
|  |           "city": "Site_c", | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "Span1ac", | ||||||
|  |       "type": "Fiber", | ||||||
|  |       "type_variety": "SSMF", | ||||||
|  |       "params": { | ||||||
|  |         "type_variety": "SSMF", | ||||||
|  |         "length": 80.0, | ||||||
|  |         "loss_coef": 0.2, | ||||||
|  |         "length_units": "km", | ||||||
|  |         "att_in": 0, | ||||||
|  |         "con_in": 0.5, | ||||||
|  |         "con_out": 0.5 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 1, | ||||||
|  |           "longitude": 0, | ||||||
|  |           "city": null, | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "Span1ca", | ||||||
|  |       "type": "Fiber", | ||||||
|  |       "type_variety": "SSMF", | ||||||
|  |       "params": { | ||||||
|  |         "type_variety": "SSMF", | ||||||
|  |         "length": 80.0, | ||||||
|  |         "loss_coef": 0.2, | ||||||
|  |         "length_units": "km", | ||||||
|  |         "att_in": 0, | ||||||
|  |         "con_in": 0.5, | ||||||
|  |         "con_out": 0.5 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 1, | ||||||
|  |           "longitude": 0, | ||||||
|  |           "city": null, | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "Span1bc", | ||||||
|  |       "type": "Fiber", | ||||||
|  |       "type_variety": "SSMF", | ||||||
|  |       "params": { | ||||||
|  |         "type_variety": "SSMF", | ||||||
|  |         "length": 80.0, | ||||||
|  |         "loss_coef": 0.2, | ||||||
|  |         "length_units": "km", | ||||||
|  |         "att_in": 0, | ||||||
|  |         "con_in": 0.5, | ||||||
|  |         "con_out": 0.5 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 1, | ||||||
|  |           "longitude": 0, | ||||||
|  |           "city": null, | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "uid": "Span1cb", | ||||||
|  |       "type": "Fiber", | ||||||
|  |       "type_variety": "SSMF", | ||||||
|  |       "params": { | ||||||
|  |         "type_variety": "SSMF", | ||||||
|  |         "length": 80.0, | ||||||
|  |         "loss_coef": 0.2, | ||||||
|  |         "length_units": "km", | ||||||
|  |         "att_in": 0, | ||||||
|  |         "con_in": 0.5, | ||||||
|  |         "con_out": 0.5 | ||||||
|  |       }, | ||||||
|  |       "metadata": { | ||||||
|  |         "location": { | ||||||
|  |           "latitude": 1, | ||||||
|  |           "longitude": 0, | ||||||
|  |           "city": null, | ||||||
|  |           "region": "" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "connections": [ | ||||||
|  |     { | ||||||
|  |       "from_node": "trx site_a", | ||||||
|  |       "to_node": "roadm site_a" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "roadm site_a", | ||||||
|  |       "to_node": "booster1 site_a" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "booster1 site_a", | ||||||
|  |       "to_node": "Span1ab" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "Span1ab", | ||||||
|  |       "to_node": "preamp site_b" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "preamp site_b", | ||||||
|  |       "to_node": "roadm site_b" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "roadm site_b", | ||||||
|  |       "to_node": "trx site_b" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "roadm site_a", | ||||||
|  |       "to_node": "booster2 site_a" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "booster2 site_a", | ||||||
|  |       "to_node": "Span2ab" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "Span2ab", | ||||||
|  |       "to_node": "preamp2 site_b" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "preamp2 site_b", | ||||||
|  |       "to_node": "roadm site_b" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "roadm site_b", | ||||||
|  |       "to_node": "booster1 site_b" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "booster1 site_b", | ||||||
|  |       "to_node": "Span1ba" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "Span1ba", | ||||||
|  |       "to_node": "preamp1 site_a" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "preamp1 site_a", | ||||||
|  |       "to_node": "roadm site_a" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "roadm site_b", | ||||||
|  |       "to_node": "booster2 site_b" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "booster2 site_b", | ||||||
|  |       "to_node": "Span2ba" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "Span2ba", | ||||||
|  |       "to_node": "preamp2 site_a" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "preamp2 site_a", | ||||||
|  |       "to_node": "roadm site_a" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "roadm site_a", | ||||||
|  |       "to_node": "booster3 site_a" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "booster3 site_a", | ||||||
|  |       "to_node": "Span1ac" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "Span1ac", | ||||||
|  |       "to_node": "preamp1 site_c" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "preamp1 site_c", | ||||||
|  |       "to_node": "roadm site_c" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "roadm site_c", | ||||||
|  |       "to_node": "booster1 site_c" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "booster1 site_c", | ||||||
|  |       "to_node": "Span1cb" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "Span1cb", | ||||||
|  |       "to_node": "preamp3 site_b" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "preamp3 site_b", | ||||||
|  |       "to_node": "roadm site_b" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "roadm site_b", | ||||||
|  |       "to_node": "booster3 site_b" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "booster3 site_b", | ||||||
|  |       "to_node": "Span1bc" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "Span1bc", | ||||||
|  |       "to_node": "preamp2 site_c" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "preamp2 site_c", | ||||||
|  |       "to_node": "roadm site_c" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "roadm site_c", | ||||||
|  |       "to_node": "booster2 site_c" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "booster2 site_c", | ||||||
|  |       "to_node": "Span1ca" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "Span1ca", | ||||||
|  |       "to_node": "preamp3 site_a" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "preamp3 site_a", | ||||||
|  |       "to_node": "roadm site_a" | ||||||
|  |     } | ||||||
|  |   ] | ||||||
|  | } | ||||||
| @@ -118,16 +118,19 @@ class Transceiver(Node): | |||||||
|         self._calc_snr(spectral_info) |         self._calc_snr(spectral_info) | ||||||
|         return spectral_info |         return spectral_info | ||||||
|  |  | ||||||
| RoadmParams = namedtuple('RoadmParams', 'target_pch_out_db add_drop_osnr restrictions') | RoadmParams = namedtuple('RoadmParams', 'target_pch_out_db add_drop_osnr restrictions per_degree_target_pch_out_db') | ||||||
|  |  | ||||||
| class Roadm(Node): | class Roadm(Node): | ||||||
|     def __init__(self, *args, params, **kwargs): |     def __init__(self, *args, params, **kwargs): | ||||||
|  |         if 'per_degree_target_pch_out_db' not in params.keys(): | ||||||
|  |             params['per_degree_target_pch_out_db'] = [] | ||||||
|         super().__init__(*args, params=RoadmParams(**params), **kwargs) |         super().__init__(*args, params=RoadmParams(**params), **kwargs) | ||||||
|         self.loss = 0 #auto-design interest |         self.loss = 0 #auto-design interest | ||||||
|         self.effective_loss = None |         self.effective_loss = None | ||||||
|         self.effective_pch_out_db = self.params.target_pch_out_db |         self.effective_pch_out_db = self.params.target_pch_out_db | ||||||
|         self.passive = True |         self.passive = True | ||||||
|         self.restrictions = self.params.restrictions |         self.restrictions = self.params.restrictions | ||||||
|  |         self.per_degree_target_pch_out_db = self.params.per_degree_target_pch_out_db | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def to_json(self): |     def to_json(self): | ||||||
| @@ -135,7 +138,8 @@ class Roadm(Node): | |||||||
|                 'type'      : type(self).__name__, |                 'type'      : type(self).__name__, | ||||||
|                 'params'    : { |                 'params'    : { | ||||||
|                     'target_pch_out_db' : self.effective_pch_out_db, |                     'target_pch_out_db' : self.effective_pch_out_db, | ||||||
|                     'restrictions'      : self.restrictions |                     'restrictions'      : self.restrictions, | ||||||
|  |                     'per_degree_target_pch_out_db': self.per_degree_target_pch_out_db | ||||||
|                     }, |                     }, | ||||||
|                 'metadata'      : { |                 'metadata'      : { | ||||||
|                     'location': self.metadata['location']._asdict() |                     'location': self.metadata['location']._asdict() | ||||||
| @@ -150,15 +154,26 @@ class Roadm(Node): | |||||||
|                           f'  effective loss (dB):  {self.effective_loss:.2f}', |                           f'  effective loss (dB):  {self.effective_loss:.2f}', | ||||||
|                           f'  pch out (dBm):        {self.effective_pch_out_db!r}']) |                           f'  pch out (dBm):        {self.effective_pch_out_db!r}']) | ||||||
|  |  | ||||||
|     def propagate(self, pref, *carriers): |     def propagate(self, pref, *carriers, degree): | ||||||
|         #pin_target and loss are read from eqpt_config.json['Roadm'] |         #pin_target and loss are read from eqpt_config.json['Roadm'] | ||||||
|         #all ingress channels in xpress are set to this power level |         #all ingress channels in xpress are set to this power level | ||||||
|         #but add channels are not, so we define an effective loss |         #but add channels are not, so we define an effective loss | ||||||
|         #in the case of add channels |         #in the case of add channels | ||||||
|         self.effective_pch_out_db = min(pref.p_spani, self.params.target_pch_out_db) |         if self.per_degree_target_pch_out_db: | ||||||
|  |             # find the target power on this degree | ||||||
|  |             try: | ||||||
|  |                 temp = next(el['target_pch_out_db'] \ | ||||||
|  |                     for el in self.per_degree_target_pch_out_db if el['to_node']==degree) | ||||||
|  |             except StopIteration: | ||||||
|  |                 # if no target power is defined on this degree use the global one | ||||||
|  |                 temp = self.params.target_pch_out_db | ||||||
|  |         else: | ||||||
|  |             # if no per degree target power are defined, use the global one | ||||||
|  |             temp = self.params.target_pch_out_db | ||||||
|  |         self.effective_pch_out_db = min(pref.p_spani, temp) | ||||||
|         self.effective_loss = pref.p_spani - self.effective_pch_out_db |         self.effective_loss = pref.p_spani - self.effective_pch_out_db | ||||||
|         carriers_power = array([c.power.signal +c.power.nli+c.power.ase for c in carriers]) |         carriers_power = array([c.power.signal +c.power.nli+c.power.ase for c in carriers]) | ||||||
|         carriers_att = list(map(lambda x : lin2db(x*1e3)-self.params.target_pch_out_db, carriers_power)) |         carriers_att = list(map(lambda x : lin2db(x*1e3)-self.effective_pch_out_db, carriers_power)) | ||||||
|         exceeding_att = -min(list(filter(lambda x: x < 0, carriers_att)), default = 0) |         exceeding_att = -min(list(filter(lambda x: x < 0, carriers_att)), default = 0) | ||||||
|         carriers_att = list(map(lambda x: db2lin(x+exceeding_att), carriers_att)) |         carriers_att = list(map(lambda x: db2lin(x+exceeding_att), carriers_att)) | ||||||
|         for carrier_att, carrier in zip(carriers_att, carriers) : |         for carrier_att, carrier in zip(carriers_att, carriers) : | ||||||
| @@ -171,8 +186,8 @@ class Roadm(Node): | |||||||
|     def update_pref(self, pref): |     def update_pref(self, pref): | ||||||
|         return pref._replace(p_span0=pref.p_span0, p_spani=self.effective_pch_out_db) |         return pref._replace(p_span0=pref.p_span0, p_spani=self.effective_pch_out_db) | ||||||
|  |  | ||||||
|     def __call__(self, spectral_info): |     def __call__(self, spectral_info, degree): | ||||||
|         carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers)) |         carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers, degree=degree)) | ||||||
|         pref = self.update_pref(spectral_info.pref) |         pref = self.update_pref(spectral_info.pref) | ||||||
|         return spectral_info._replace(carriers=carriers, pref=pref) |         return spectral_info._replace(carriers=carriers, pref=pref) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -233,7 +233,7 @@ def prev_node_generator(network, node): | |||||||
|     except StopIteration: |     except StopIteration: | ||||||
|         raise NetworkTopologyError(f'Node {node.uid} is not properly connected, please check network topology') |         raise NetworkTopologyError(f'Node {node.uid} is not properly connected, please check network topology') | ||||||
|     # yield and re-iterate |     # yield and re-iterate | ||||||
|     if isinstance(prev_node, Fused) or isinstance(node, Fused): |     if isinstance(prev_node, Fused) or isinstance(node, Fused) and not isinstance(prev_node, Roadm): | ||||||
|         yield prev_node |         yield prev_node | ||||||
|         yield from prev_node_generator(network, prev_node) |         yield from prev_node_generator(network, prev_node) | ||||||
|     else: |     else: | ||||||
| @@ -247,7 +247,7 @@ def next_node_generator(network, node): | |||||||
|     except StopIteration: |     except StopIteration: | ||||||
|         raise NetworkTopologyError('Node {node.uid} is not properly connected, please check network topology') |         raise NetworkTopologyError('Node {node.uid} is not properly connected, please check network topology') | ||||||
|     # yield and re-iterate |     # yield and re-iterate | ||||||
|     if isinstance(next_node, Fused) or isinstance(node, Fused): |     if isinstance(next_node, Fused) or isinstance(node, Fused) and not isinstance(next_node, Roadm): | ||||||
|         yield next_node |         yield next_node | ||||||
|         yield from next_node_generator(network, next_node) |         yield from next_node_generator(network, next_node) | ||||||
|     else: |     else: | ||||||
| @@ -315,6 +315,17 @@ def set_egress_amplifier(network, roadm, equipment, pref_total_db): | |||||||
|         #     node = find_last_node(next_node) |         #     node = find_last_node(next_node) | ||||||
|         #     next_node = next(n for n in network.successors(node)) |         #     next_node = next(n for n in network.successors(node)) | ||||||
|         #     next_node = find_last_node(next_node) |         #     next_node = find_last_node(next_node) | ||||||
|  |          | ||||||
|  |         if node.per_degree_target_pch_out_db: | ||||||
|  |             # find the target power on this degree | ||||||
|  |             try: | ||||||
|  |                 prev_dp = next(el["target_pch_out_db"] for el in \ | ||||||
|  |                     node.per_degree_target_pch_out_db if el["to_node"]==next_node.uid) | ||||||
|  |             except StopIteration: | ||||||
|  |                 # if no target power is defined on this degree use the global one | ||||||
|  |                 prev_dp = getattr(node.params, 'target_pch_out_db', 0) | ||||||
|  |         else: | ||||||
|  |             # if no per degree target power is given use the global one | ||||||
|             prev_dp = getattr(node.params, 'target_pch_out_db', 0) |             prev_dp = getattr(node.params, 'target_pch_out_db', 0) | ||||||
|         dp = prev_dp |         dp = prev_dp | ||||||
|         prev_voa = 0 |         prev_voa = 0 | ||||||
|   | |||||||
| @@ -122,6 +122,15 @@ BLOCKING_NOPATH = ['NO_PATH', 'NO_PATH_WITH_CONSTRAINT',\ | |||||||
| BLOCKING_NOMODE = ['NO_FEASIBLE_MODE', 'MODE_NOT_FEASIBLE'] | BLOCKING_NOMODE = ['NO_FEASIBLE_MODE', 'MODE_NOT_FEASIBLE'] | ||||||
| BLOCKING_NOSPECTRUM = 'NO_SPECTRUM' | BLOCKING_NOSPECTRUM = 'NO_SPECTRUM' | ||||||
|  |  | ||||||
|  | def element_to_node_type(element): | ||||||
|  |     if isinstance(element, Transceiver): | ||||||
|  |         return "transceiver" | ||||||
|  |     if isinstance(element, Edfa): | ||||||
|  |         return "EDFA" | ||||||
|  |     if isinstance(element, Roadm): | ||||||
|  |         return "ROADM" | ||||||
|  |     return None | ||||||
|  |  | ||||||
| class Result_element(Element): | class Result_element(Element): | ||||||
|     def __init__(self, path_request, computed_path, reversed_computed_path=None): |     def __init__(self, path_request, computed_path, reversed_computed_path=None): | ||||||
|         self.path_id = path_request.request_id |         self.path_id = path_request.request_id | ||||||
| @@ -131,13 +140,13 @@ class Result_element(Element): | |||||||
|         if reversed_computed_path is not None: |         if reversed_computed_path is not None: | ||||||
|             self.reversed_computed_path = reversed_computed_path |             self.reversed_computed_path = reversed_computed_path | ||||||
|     uid = property(lambda self: repr(self)) |     uid = property(lambda self: repr(self)) | ||||||
|     @property |  | ||||||
|     def detailed_path_json(self): |     def detailed_path_json(self, path): | ||||||
|         """ a function that builds path object for normal and blocking cases |         """ a function that builds path object for normal and blocking cases | ||||||
|         """ |         """ | ||||||
|         index = 0 |         index = 0 | ||||||
|         pro_list = [] |         pro_list = [] | ||||||
|         for element in self.computed_path: |         for element in path: | ||||||
|             temp = { |             temp = { | ||||||
|                 'path-route-object': { |                 'path-route-object': { | ||||||
|                     'index': index, |                     'index': index, | ||||||
| @@ -148,6 +157,9 @@ class Result_element(Element): | |||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |             node_type = element_to_node_type(element) | ||||||
|  |             if (node_type is not None): | ||||||
|  |                 temp['path-route-object']['num-unnum-hop']['gnpy-node-type'] = node_type | ||||||
|             pro_list.append(temp) |             pro_list.append(temp) | ||||||
|             index += 1 |             index += 1 | ||||||
|             if self.path_request.M > 0: |             if self.path_request.M > 0: | ||||||
| @@ -180,6 +192,31 @@ class Result_element(Element): | |||||||
|                     } |                     } | ||||||
|                 pro_list.append(temp) |                 pro_list.append(temp) | ||||||
|                 index += 1 |                 index += 1 | ||||||
|  |             if isinstance(element, Roadm): | ||||||
|  |                 temp = { | ||||||
|  |                     'path-route-object': { | ||||||
|  |                         'index': index, | ||||||
|  |                         'target-channel-power' : { | ||||||
|  |                            'value' : element.effective_pch_out_db, | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 pro_list.append(temp) | ||||||
|  |                 index += 1 | ||||||
|  |             if isinstance(element, Edfa): | ||||||
|  |                 temp = { | ||||||
|  |                     'path-route-object': { | ||||||
|  |                         'index': index, | ||||||
|  |                         'target-channel-power' : { | ||||||
|  |                             'value': element.effective_pch_out_db, | ||||||
|  |                         }, | ||||||
|  |                         'output-voa':  { | ||||||
|  |                             'value': element.out_voa, | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 pro_list.append(temp) | ||||||
|  |                 index += 1 | ||||||
|         return pro_list |         return pro_list | ||||||
|     @property |     @property | ||||||
|     def path_properties(self): |     def path_properties(self): | ||||||
| @@ -218,12 +255,13 @@ class Result_element(Element): | |||||||
|             path_properties = { |             path_properties = { | ||||||
|                 'path-metric': path_metric(self.computed_path, self.path_request), |                 'path-metric': path_metric(self.computed_path, self.path_request), | ||||||
|                 'z-a-path-metric': path_metric(self.reversed_computed_path, self.path_request), |                 'z-a-path-metric': path_metric(self.reversed_computed_path, self.path_request), | ||||||
|                 'path-route-objects': self.detailed_path_json |                 'path-route-objects': self.detailed_path_json(self.computed_path), | ||||||
|  |                 'reversed-path-route-objects': self.detailed_path_json(self.reversed_computed_path), | ||||||
|                 } |                 } | ||||||
|         else: |         else: | ||||||
|             path_properties = { |             path_properties = { | ||||||
|                 'path-metric': path_metric(self.computed_path, self.path_request), |                 'path-metric': path_metric(self.computed_path, self.path_request), | ||||||
|                 'path-route-objects': self.detailed_path_json |                 'path-route-objects': self.detailed_path_json(self.computed_path) | ||||||
|                 } |                 } | ||||||
|         return path_properties |         return path_properties | ||||||
|  |  | ||||||
| @@ -383,8 +421,13 @@ def propagate(path, req, equipment): | |||||||
|     si = create_input_spectral_information( |     si = create_input_spectral_information( | ||||||
|         req.f_min, req.f_max, req.roll_off, req.baud_rate, |         req.f_min, req.f_max, req.roll_off, req.baud_rate, | ||||||
|         req.power, req.spacing) |         req.power, req.spacing) | ||||||
|     for el in path: |     for i, el in enumerate(path): | ||||||
|  |         if isinstance(el, Roadm): | ||||||
|  |             next_el = path[i+1] | ||||||
|  |             si = el(si, degree=next_el.uid) | ||||||
|  |         else: | ||||||
|             si = el(si) |             si = el(si) | ||||||
|  |         print(el) | ||||||
|     path[-1].update_snr(req.tx_osnr, equipment['Roadm']['default'].add_drop_osnr) |     path[-1].update_snr(req.tx_osnr, equipment['Roadm']['default'].add_drop_osnr) | ||||||
|     return path |     return path | ||||||
|  |  | ||||||
| @@ -393,8 +436,12 @@ def propagate2(path, req, equipment): | |||||||
|         req.f_min, req.f_max, req.roll_off, req.baud_rate, |         req.f_min, req.f_max, req.roll_off, req.baud_rate, | ||||||
|         req.power, req.spacing) |         req.power, req.spacing) | ||||||
|     infos = {} |     infos = {} | ||||||
|     for el in path: |     for i, el in enumerate(path): | ||||||
|         before_si = si |         before_si = si | ||||||
|  |         if isinstance(el, Roadm): | ||||||
|  |             next_el = path[i+1] | ||||||
|  |             after_si  = si = el(si, degree=next_el.uid) | ||||||
|  |         else: | ||||||
|             after_si  = si = el(si) |             after_si  = si = el(si) | ||||||
|         infos[el] = before_si, after_si |         infos[el] = before_si, after_si | ||||||
|     path[-1].update_snr(req.tx_osnr, equipment['Roadm']['default'].add_drop_osnr) |     path[-1].update_snr(req.tx_osnr, equipment['Roadm']['default'].add_drop_osnr) | ||||||
| @@ -424,7 +471,11 @@ def propagate_and_optimize_mode(path, req, equipment): | |||||||
|             spc_info = create_input_spectral_information(req.f_min, req.f_max, |             spc_info = create_input_spectral_information(req.f_min, req.f_max, | ||||||
|                                                    equipment['SI']['default'].roll_off, |                                                    equipment['SI']['default'].roll_off, | ||||||
|                                                    this_br, req.power, req.spacing) |                                                    this_br, req.power, req.spacing) | ||||||
|             for el in path: |             for i, el in enumerate(path): | ||||||
|  |                 if isinstance(el, Roadm): | ||||||
|  |                     next_el = path[i+1] | ||||||
|  |                     spc_info = el(spc_info, degree=next_el.uid) | ||||||
|  |                 else: | ||||||
|                     spc_info = el(spc_info) |                     spc_info = el(spc_info) | ||||||
|             for this_mode in modes_to_explore: |             for this_mode in modes_to_explore: | ||||||
|                 if path[-1].snr is not None: |                 if path[-1].snr is not None: | ||||||
|   | |||||||
| @@ -59,6 +59,4 @@ | |||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
|       } |       } | ||||||
|     } |     },       | ||||||
|   ] |  | ||||||
| } |  | ||||||
| @@ -1,5 +1,7 @@ | |||||||
| alabaster>=0.7.12,<1 | alabaster>=0.7.12,<1 | ||||||
| docutils==0.15.2 | docutils==0.15.2 | ||||||
|  | flask==1.0.2 | ||||||
|  | flask-restful==0.3.7 | ||||||
| matplotlib>=3.1.0,<4 | matplotlib>=3.1.0,<4 | ||||||
| networkx>=2.3,<3 | networkx>=2.3,<3 | ||||||
| numpy>=1.16.1,<2 | numpy>=1.16.1,<2 | ||||||
|   | |||||||
| @@ -47,24 +47,25 @@ | |||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
|           "trx_type": null, |           "trx_type": "name of the tsp type_variety as listed in the library", | ||||||
|           "trx_mode": null, |           "trx_mode": "optional, name of the mode as listed in the tsp type_variety", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "n": "null", | ||||||
|               "m": "null" |               "m": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": null, |           "spacing": mandatory decimal Hz, | ||||||
|           "max-nb-of-channel": null, |           "max-nb-of-channel": optional integer, | ||||||
|           "output-power": null, |           "output-power": optional decimal W, | ||||||
|           "path_bandwidth": null |           "path_bandwidth": optional bit/s | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }], |     } | ||||||
|   "synchronization": [ |   ], | ||||||
|  |   "synchronization": [   list of disjunctions, optional | ||||||
|     { |     { | ||||||
|       "synchronization-id": null, |       "synchronization-id": "3", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "True", |         "relaxable": "True", | ||||||
|         "disjointness": "node link", |         "disjointness": "node link", | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user