mirror of
				https://github.com/Telecominfraproject/oopt-gnpy.git
				synced 2025-10-30 09:42:22 +00:00 
			
		
		
		
	Compare commits
	
		
			22 Commits
		
	
	
		
			v2.12.1
			...
			experiment
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | d201ec26bf | ||
|   | 183639ba07 | ||
|   | 20c6d5cf95 | ||
|   | 2b38e677b2 | ||
|   | dab521a99e | ||
|   | 6cc55b83e5 | ||
|   | ddbb9b5af7 | ||
|   | dd3d2e1152 | ||
|   | 936b17c151 | ||
|   | 5f38db2d2f | ||
|   | 7172ceab20 | ||
|   | 7874ad61af | ||
|   | cddebd55a1 | ||
|   | aa96694c19 | ||
|   | 1ed81a6fd9 | ||
|   | f611f3d899 | ||
|   | f919bbea41 | ||
|   | 4202d85260 | ||
|   | d5ca3fe6f6 | ||
|   | e5efdc0138 | ||
|   | 4fe77b2519 | ||
|   | 1c971dbaeb | 
| @@ -3,8 +3,6 @@ os: linux | ||||
| language: python | ||||
| services: docker | ||||
| python: | ||||
|   - "3.6" | ||||
|   - "3.7" | ||||
|   - "3.8" | ||||
|   - "3.9" | ||||
| before_install: | ||||
|   | ||||
| @@ -13,7 +13,6 @@ | ||||
|               coverage_job_name_current: tox-py38-cover | ||||
|         - tox-linters-diff-n-report: | ||||
|             voting: false | ||||
|         - tox-py36-el8 | ||||
|         - tox-docs-f32 | ||||
|         - tox-py38-cover-previous | ||||
|     gate: | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| .. _excel: | ||||
|  | ||||
| Excel (XLS, XLSX) input files | ||||
| ============================= | ||||
|  | ||||
|   | ||||
							
								
								
									
										6
									
								
								docs/gnpy-api-yang.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								docs/gnpy-api-yang.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| ``gnpy.yang`` | ||||
| ------------- | ||||
|  | ||||
| .. automodule:: gnpy.yang | ||||
| .. automodule:: gnpy.yang.conversion | ||||
| .. automodule:: gnpy.yang.io | ||||
| @@ -12,3 +12,4 @@ API Reference Documentation | ||||
|    gnpy-api-core | ||||
|    gnpy-api-topology | ||||
|    gnpy-api-tools | ||||
|    gnpy-api-yang | ||||
|   | ||||
| @@ -13,6 +13,7 @@ in real-world mesh optical networks. It is based on the Gaussian Noise Model. | ||||
|    install | ||||
|    json | ||||
|    excel | ||||
|    yang | ||||
|    extending | ||||
|    about-project | ||||
|    model | ||||
|   | ||||
| @@ -38,7 +38,7 @@ Using Python on your computer | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
|    **Note**: `gnpy` supports Python 3 only. Python 2 is not supported. | ||||
|    `gnpy` requires Python ≥3.6 | ||||
|    `gnpy` requires Python ≥3.8 | ||||
|  | ||||
|    **Note**: the `gnpy` maintainers strongly recommend the use of Anaconda for | ||||
|    managing dependencies. | ||||
| @@ -84,7 +84,7 @@ exact version of Python you are using. | ||||
|     $ which python                   # check which Python executable is used | ||||
|     /path/to/anaconda/bin/python | ||||
|     $ python -V                      # check your Python version | ||||
|     Python 3.6.5 :: Anaconda, Inc. | ||||
|     Python 3.8.0 :: Anaconda, Inc. | ||||
|  | ||||
| .. _install-pip: | ||||
|  | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| .. _legacy-json: | ||||
|  | ||||
| JSON Input Files | ||||
| ================ | ||||
|  | ||||
|   | ||||
							
								
								
									
										598
									
								
								docs/yang.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										598
									
								
								docs/yang.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,598 @@ | ||||
| (yang)= | ||||
| # YANG-formatted data | ||||
|  | ||||
| (yang-equipment)= | ||||
| ## Equipment Library | ||||
|  | ||||
| The [equipment library](concepts-equipment) is defined via the `tip-photonic-equipment` YANG model. | ||||
| The database describes all [amplifier models](yang-equipment-amplifier), all [types of fiber](yang-equipment-fiber), all possible [ROADM models](yang-equipment-roadm), etc. | ||||
|  | ||||
| (yang-equipment-amplifier)= | ||||
| ### Amplifiers | ||||
|  | ||||
| Amplifiers introduce noise to the signal during amplification, and care must be taken to describe their performance correctly. | ||||
| There are some common input parameters: | ||||
|  | ||||
| `type` | ||||
|  | ||||
| : A free-form name which must be unique within the whole equipment library. | ||||
| It will be used in the network topology to specify which amplifier model is deployed at the given place in the network. | ||||
|  | ||||
| `frequency-min` and `frequency-max` | ||||
|  | ||||
| : Operating range of the amplifier. | ||||
|  | ||||
| `gain-flatmax` | ||||
|  | ||||
| :   The optimal operating point of the amplifier. | ||||
| This is the place where the gain tilt and the NF of the amplifier are at its best. | ||||
|  | ||||
| `gain-min` | ||||
|  | ||||
| : Minimal possible gain that can be set for the EDFA. | ||||
| Any lower gain requires adding a physical attenuator. | ||||
|  | ||||
| `max-power-out` | ||||
|  | ||||
| : Total power cap at the output of the amplifier, measured across the whole spectrum. | ||||
|  | ||||
| `has-output-voa` | ||||
|  | ||||
| : Specifies if there's a Variable Optical Attenuator (VOA) at the EDFA's output port. | ||||
|  | ||||
| One of the key parameters of an amplifier is the method to use for [computing the Noise Figure (NF)](concepts-nf-model). | ||||
| Here's how they are represented in YANG data: | ||||
|  | ||||
| (yang-equipment-amplifier-polynomial-NF)= | ||||
| #### `polynomial-NF` | ||||
|  | ||||
| The [Polynomial NF model](ext-nf-model-polynomial-NF) requires four coefficients for the polynomial function: `a`, `b`, `c` and `d`. | ||||
|  | ||||
| ```json | ||||
| { | ||||
|   "type": "Juniper-BoosterHG", | ||||
|   "gain-min": "10", | ||||
|   "gain-flatmax": "25", | ||||
|   "max-power-out": "21", | ||||
|   "frequency-min": "191.35", | ||||
|   "frequency-max": "196.1", | ||||
|   "polynomial-NF": { | ||||
|     "a": "0.0008", | ||||
|     "b": "0.0272", | ||||
|     "c": "-0.2249", | ||||
|     "d": "6.4902" | ||||
|   } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| (yang-equipment-amplifier-min-max-NF)= | ||||
| #### `min-max-NF` | ||||
|  | ||||
| This is an operator-focused model. | ||||
| Performance is defined by the [minimal and maximal NF](nf-model-min-max-NF). | ||||
|  | ||||
| `nf-min` | ||||
|  | ||||
| : Minimal Noise Figure. | ||||
| This is achieved when the EDFA operates at its maximal flat gain (see the `gain-flatmax` parameter). | ||||
|  | ||||
| `nf-max` | ||||
|  | ||||
| : Maximal Noise Figure. | ||||
| This worst-case scenario applies when the EDFA operates at its minimal gain (see the `gain-min` parameter). | ||||
|  | ||||
| (yang-equipment-amplifier-openroadm)= | ||||
| #### OpenROADM | ||||
|  | ||||
| NF models for preamps, boosters and inline amplifiers as defined via the OpenROADM group. | ||||
|  | ||||
| (yang-equipment-amplifier-polynomial-OSNR-OpenROADM)= | ||||
| ##### `OpenROADM-ILA` | ||||
|  | ||||
| This model is useful for [amplifiers compliant to the OpenROADM specification for ILA](ext-nf-model-polynomial-OSNR-OpenROADM). | ||||
| The input parameters to this model are once again four coefficients `a`. `b`, `c` and `d`: | ||||
|  | ||||
| ```json | ||||
| { | ||||
|   "type": "low-noise", | ||||
|   "gain-min": "12", | ||||
|   "gain-flatmax": "27", | ||||
|   "max-power-out": "22", | ||||
|   "frequency-min": "191.35", | ||||
|   "frequency-max": "196.1", | ||||
|   "OpenROADM-ILA": { | ||||
|     "a": "-8.104e-4", | ||||
|     "b": "-6.221e-2", | ||||
|     "c": "-5.889e-1", | ||||
|     "d": "37.62", | ||||
|   } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| (yang-equipment-amplifier-OpenROADM-preamp-booster)= | ||||
| ##### `OpenROADM-preamp` and `OpenROADM-booster` | ||||
|  | ||||
| No extra parameters are defined for these NF models. | ||||
| See the [model documentation](ext-nf-model-noise-mask-OpenROADM) for details. | ||||
|  | ||||
| (yang-equipment-amplifier-composite)= | ||||
| #### `composite` | ||||
|  | ||||
| A [composite](ext-nf-model-dual-stage-amplifier) amplifier combines two distinct amplifiers. | ||||
| The first amplifier will be always operated at its maximal gain (and therefore its best NF). | ||||
|  | ||||
| `preamp` | ||||
|  | ||||
| : Reference to the first amplifier model | ||||
|  | ||||
| `booster` | ||||
|  | ||||
| : Reference to the second amplifier model | ||||
|  | ||||
| (yang-equipment-amplifier-raman-approximation)= | ||||
| #### `raman-approximation` | ||||
|  | ||||
| A fixed-NF amplifier, especially suitable for emulating Raman amplifiers | ||||
| in scenarios where the Raman-aware engine cannot be used. | ||||
|  | ||||
| `nf` | ||||
|  | ||||
| : Noise Figure of the amplifier. | ||||
|  | ||||
| (yang-equipment-amplifier-fine-tuning)= | ||||
| #### Advanced EDFA parameters | ||||
|  | ||||
| In addition to all parameters specified above, it is also possible to describe the EDFA\'s performance in higher detail. | ||||
| All of the following parameters are given as measurement points at arbitrary frequencies. | ||||
| The more data points provided, the more accurate is the simulation. | ||||
| The underlying model uses piecewise linear approximation to estimate values which are laying in between the provided values. | ||||
|  | ||||
| `dynamic-gain-tilt` | ||||
|  | ||||
| : FIXME: document this | ||||
|  | ||||
| `gain-ripple` | ||||
|  | ||||
| : Difference of the amplifier gain for a specified frequency, as compared to the typical gain over the whole spectrum | ||||
|  | ||||
| `nf-ripple` | ||||
|  | ||||
| : Difference in the resulting Noise Figure (NF) as a function of a carrier frequency | ||||
|  | ||||
| ```json | ||||
| { | ||||
|   "type": "vg-15-26", | ||||
|   "gain-min": "15", | ||||
|   "gain-flatmax": "26", | ||||
|   "dynamic-gain-tilt": [ | ||||
|     { | ||||
|       "frequency": "191.35", | ||||
|       "dynamic-gain-tilt": "0" | ||||
|     }, | ||||
|     { | ||||
|       "frequency": "196.1", | ||||
|       "dynamic-gain-tilt": "2.4" | ||||
|     } | ||||
|   ], | ||||
|   "max-power-out": "23", | ||||
|   "min-max-NF": { | ||||
|     "nf-min": "6.0", | ||||
|     "nf-max": "10.0" | ||||
|   } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| These values are optional. If not provided, gain and NF is assumed to not vary with carrier frequency. | ||||
|  | ||||
| (yang-equipment-fiber)= | ||||
| ### Fiber | ||||
|  | ||||
| An optical fiber attenuates the signal and acts as a medium for non-linear interference (NLI) for all signals in the propagated spectrum. | ||||
| When using the Raman-aware simulation engine, the Raman effect is also considered. | ||||
|  | ||||
| `type` | ||||
|  | ||||
| : A free-form name which must be unique within the whole equipment library, such as `G.652`. | ||||
|  | ||||
| `chromatic-dispersion` | ||||
|  | ||||
| : Chromatic dispersion, in $\frac{ps}{nm\times km}$. | ||||
|  | ||||
| `chromatic-dispersion-slope` | ||||
|  | ||||
| : Dispersion slope is related to the $\beta _3$ coefficient. | ||||
| In $\frac{ps}{nm^{2}\times km}$. | ||||
|  | ||||
| `gamma` | ||||
|  | ||||
| : Fiber\'s $\gamma$ coefficient. | ||||
| In $\frac{1}{W\times km}$. | ||||
|  | ||||
| `pmd-coefficient` | ||||
|  | ||||
| : Coefficient for the Polarization Mode Dispersion (PMD). | ||||
| In $\frac{ps}{\sqrt{km}}$. | ||||
|  | ||||
| `raman-efficiency` | ||||
|  | ||||
| : Normalized efficiency of the Raman amplification per operating frequency. | ||||
| This is a required parameter if using Rama-aware simulation engine. | ||||
| The data type is a YANG list keyed by `delta-frequency` (in $\text{THz}$).  | ||||
| For each `delta-frequency`, provide the `cr` parameter which is a dimensionless number indicating how effective the Raman transfer of energy is at that particular frequency offset from the pumping signal. | ||||
|  | ||||
| ```javascript | ||||
| { | ||||
|   "type": "SSMF", | ||||
|   "dispersion": "16.7", | ||||
|   "gamma": "1.27", | ||||
|   "pmd-coefficient": "0.0400028124", | ||||
|   "raman-efficiency": [ | ||||
|     { | ||||
|       "delta-frequency": "0", | ||||
|       "cr": "0" | ||||
|     }, | ||||
|     { | ||||
|       "delta-frequency": "0.5", | ||||
|       "cr": "9.4e-06" | ||||
|     }, | ||||
|  | ||||
|     // more frequencies go here | ||||
|  | ||||
|     { | ||||
|       "delta-frequency": "42.0", | ||||
|       "cr": "1e-07" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| ``` | ||||
| (yang-equipment-roadm)= | ||||
| ### ROADMs | ||||
|  | ||||
| Compared to EDFAs and fibers, ROADM descriptions are simpler.  | ||||
| In GNPy, ROADM mainly acts as a smart, spectrum-specific attenuator which equalizes carrier power to a specified power level.  | ||||
| The PMD contribution is also taken into account, and the Add and Drop stages affect signal\'s OSNR as well. | ||||
|  | ||||
| `type` | ||||
|  | ||||
| : Unique model identification, used when cross-referencing from the network topology. | ||||
|  | ||||
| `add-drop-osnr` | ||||
|  | ||||
| : OSNR penalty introduced by the Add stage or the Drop stage of this ROADM type. | ||||
|  | ||||
| `target-channel-out-power` | ||||
|  | ||||
| : Per-channel target TX power towards the egress amplifier. | ||||
| Within GNPy, a ROADM is expected to attenuate any signal that enters the ROADM node to this level.  | ||||
| This can be overridden on a per-link in the network topology. | ||||
|  | ||||
| `pmd` | ||||
|  | ||||
| : Polarization mode dispersion (PMD) penalty of the express path within this ROADM model.  | ||||
| In $\text{s}$. | ||||
|  | ||||
| `compatible-preamp` and `compatible-booster` | ||||
|  | ||||
| : List of all allowed booster/preamplifier types.  | ||||
| Useful for specifying constraints on what amplifier modules fit into ROADM chassis, and when using fully disaggregated ROADM topologies as well. | ||||
|  | ||||
| (yang-equipment-transponder)= | ||||
| ### Transponders | ||||
|  | ||||
| Transponders (or transceivers) are sources and detectors of optical signals. | ||||
| There are a few parameters which apply to a transponder model: | ||||
|  | ||||
| `type` | ||||
|  | ||||
| : Unique name, for corss-referencing from the topology data. | ||||
|  | ||||
| `frequency-min` and `frequency-max` | ||||
|  | ||||
| : Minimal and maximal operating frequencies of the receiver and transmitter. | ||||
|  | ||||
| A lot of transponders can operate in a variety of modes, which are described via the `transceiver/mode` list: | ||||
|  | ||||
| `name` | ||||
|  | ||||
| : Identification of the transmission mode.  | ||||
| Free form, has to be unique within one transponder type. | ||||
|  | ||||
| `bit-rate` | ||||
|  | ||||
| : Data bit rate, in $\text{Gbits}\times s^{-1}$. | ||||
|  | ||||
| `baud-rate` | ||||
|  | ||||
| : Symbol modulation rate, in $\text{Gbaud}$. | ||||
|  | ||||
| `required-osnr` | ||||
|  | ||||
| : Minimal allowed OSNR for the receiver. | ||||
|  | ||||
| `in-band-tx-osnr` | ||||
|  | ||||
| : Worst-case guaranteed initial OSNR at the Tx port per 0.1nm of bandwidth | ||||
| Only the in-band OSNR is considered. | ||||
|  | ||||
| `grid-spacing` | ||||
|  | ||||
| : Minimal grid spacing, i.e., an effective channel spectral bandwidth. | ||||
| In $\text{Hz}$. | ||||
|  | ||||
| `tx-roll-off` | ||||
|  | ||||
| : Roll-off parameter ($\beta$) of the TX pulse shaping filter. | ||||
| This assumes a raised-cosine filter. | ||||
|  | ||||
| (yang-simulation)= | ||||
| ## Simulation Parameters | ||||
|  | ||||
| The `tip-photonic-simulation` model holds options which control how a simulation behaves. | ||||
| These include information such as the spectral allocation to work on, the initial launch power, or the desired precision of the Raman engine. | ||||
|  | ||||
| ### Propagated spectrum | ||||
|  | ||||
| Channel allocation is controlled via `/tip-photonic-simulation:simulation/grid`. | ||||
| This input structure does not support flexgrid (yet), and it assumes homogeneous channel allocation in a worst-case scenario (all channels allocated): | ||||
|  | ||||
| `frequency-min` and `frequency-max` | ||||
|  | ||||
| : Define the range of central channel frequencies. | ||||
|  | ||||
| `spacing` | ||||
|  | ||||
| : How far apart from each other to place channels. | ||||
|  | ||||
| `baud-rate` | ||||
|  | ||||
| : Modulation speed. | ||||
|  | ||||
| `power` | ||||
|  | ||||
| : Launch power, per-channel. | ||||
|  | ||||
| `tx-osnr` | ||||
|  | ||||
| : The initial OSNR of a signal at the transponder's TX port. | ||||
|  | ||||
| `tx-roll-off` | ||||
|  | ||||
| : Roll-off parameter (β) of the TX pulse shaping filter. | ||||
| This assumes a raised-cosine filter. | ||||
|  | ||||
| ### Autodesign | ||||
|  | ||||
| Autodesign is controlled via `/tip-photonic-simulation:autodesign`. | ||||
|  | ||||
| `power-adjustment-for-span-loss` | ||||
|  | ||||
| : This adjusts the launch power of each span depending on the span's loss. | ||||
| When in effect, launch powers to spans are adjusted based on the total span loss. | ||||
| The span loss is compared to a reference span of 20 dB, and the launch power is adjusted by about 0.3 * `loss_difference`, up to a provided maximal adjustment. | ||||
| This adjustment is performed for all spans when running in the `power-mode` (see below). | ||||
| When in `gain-mode`, it affects only EDFAs which do not have an explicitly assigned `delta-p`. | ||||
|  | ||||
| FIXME: there are more. | ||||
|  | ||||
| #### Power mode | ||||
|  | ||||
| FIXME: This is currently mostly undocumented. | ||||
| Sorry. | ||||
|  | ||||
| In power mode, GNPy can try out several initial launch powers. | ||||
| This is controlled via the `/tip-photonic-simulation:autodesign/power-mode/power-sweep`: | ||||
|  | ||||
| `start` | ||||
|  | ||||
| : Initial delta from the reference power when determining the best initial launch power. | ||||
|  | ||||
| `stop` | ||||
|  | ||||
| : Final delta from the reference power when determining the best initial launch power | ||||
|  | ||||
| `step-size` | ||||
|  | ||||
| : Step size when determining the best initial launch power | ||||
|  | ||||
| #### Gain mode | ||||
|  | ||||
| FIXME: This is currently mostly undocumented. | ||||
| Sorry. | ||||
|  | ||||
| In the gain mode, EDFA gain is based on the previous span loss. | ||||
| For all EDFAs whose gain has not been set manually, set the gain based on the following rules: | ||||
|  | ||||
| 1) Set gain to the preceding span loss. | ||||
| 2) Offset the gains around the reference power (FIXME: what does it mean? | ||||
|  | ||||
| This will leave the gain of EDFAs which have their gains set manually in the network topology unchanged. | ||||
|  | ||||
| ### Miscellaneous parameters | ||||
|  | ||||
| `/tip-photonic-simulation:system-margin` | ||||
|  | ||||
| : How many $\text{dB}$ of headroom to require. | ||||
| This parameter is useful to account for component aging, fiber repairs, etc. | ||||
|  | ||||
| (yang-topology)= | ||||
| ## Network Topology | ||||
|  | ||||
| The *topology* acts as a "digital self" of the simulated network. | ||||
| The topology builds upon the `ietf-network-topology` from [RFC8345](https://tools.ietf.org/html/rfc8345#section-4.2) and is implemented in the `tip-photonic-topology` YANG model. | ||||
|  | ||||
| In this network, the *nodes* correspond to [amplifiers](yang-topology-amplifier), [ROADMs](yang-topology-roadm), [transceivers](yang-topology-transceiver) and [attenuators](yang-topology-attenuator). | ||||
| The *links* model [optical fiber](yang-topology-fiber) or [patchcords](yang-topology-patch)). | ||||
| Additional elements are also available for modeling networks which have not been fully specified yet. | ||||
|  | ||||
| Where not every amplifier has been placed already, some links can be represented by a [tentative-link](yang-topology-tentative-link), and some amplifier nodes by [placeholders](yang-topology-amplifier-placeholder). | ||||
|  | ||||
| (yang-topology-common-node-props)= | ||||
| ### Common Node Properties | ||||
|  | ||||
| All *nodes* share a common set of properties for describing their physical location. | ||||
| These are useful mainly for visualizing the network topology. | ||||
|  | ||||
| ```javascript | ||||
|   { | ||||
|     "node-id": "123", | ||||
|  | ||||
|     // ...more data go here... | ||||
|  | ||||
|     "tip-photonic-topology:geo-location": { | ||||
|       "x": "0.5", | ||||
|       "y": "0.0" | ||||
|     } | ||||
|   } | ||||
| ``` | ||||
|  | ||||
| Below is a reference as to how the individual elements are used. | ||||
|  | ||||
| (yang-topology-amplifier)= | ||||
| ### Amplifiers | ||||
|  | ||||
| A physical, unidirectional amplifier. | ||||
| The amplifier *model* is specified via `tip-photonic-topology:amplifier/model` leafref. | ||||
|  | ||||
| #### Operational data | ||||
|  | ||||
| If not set, GNPy determines the optimal operating point of the amplifier for the specified simulation input parameters so that the total GSNR remains at its highest possible value. | ||||
|  | ||||
| `out-voa-target` | ||||
|  | ||||
| : Attenuation of the output VOA | ||||
|  | ||||
| `gain-target` | ||||
|  | ||||
| : Amplifier gain | ||||
|  | ||||
| `tilt-target` | ||||
|  | ||||
| : Amplifier tilt | ||||
|  | ||||
| #### Example | ||||
|  | ||||
| ```json | ||||
|   { | ||||
|     "node-id": "edfa-A", | ||||
|     "tip-photonic-topology:amplifier": { | ||||
|       "model": "fixed-22", | ||||
|       "out-voa-target": "0.0", | ||||
|       "gain-target": "19.0", | ||||
|       "tilt-target": "10.0" | ||||
|     } | ||||
|   } | ||||
| ``` | ||||
|  | ||||
| (yang-topology-transceiver)= | ||||
| ### Transceivers | ||||
|  | ||||
| Transceivers can be used as source and destination points of a path when requesting connectivity feasibility checks. | ||||
|  | ||||
| `model` | ||||
|  | ||||
| : Cross-reference to the equipment library, specifies the physical model of this transponder. | ||||
|  | ||||
| There are no transceiver-specific parameters. | ||||
| Mode selection is done via global simulation parameters. | ||||
|  | ||||
| (yang-topology-roadm)= | ||||
| ### ROADMs | ||||
|  | ||||
| FIXME: topology | ||||
|  | ||||
| (yang-topology-attenuator)= | ||||
| ### Attenuators | ||||
|  | ||||
| This element (``attenuator``) is suitable for modeling a concentrated loss -- perhaps a real-world long-haul fiber with a splice that has a significant attenuation. | ||||
| Only one attribute is defined: | ||||
|  | ||||
| `attenuation` | ||||
|  | ||||
| : Attenuation of the splice, in $\text{dB}$. | ||||
|  | ||||
| In the original data formed used by GNPy, the corresponding element, `Fused`, was often used as a cue which disabled automatic EDFA placement. | ||||
|  | ||||
| (yang-topology-amplifier-placeholder)= | ||||
| ### Amplifier Placeholders | ||||
|  | ||||
| In cases where the actual amplifier locations are already known, but a specific type of amplifier has not been decided yet, the `amplifier-placeholder` will be used. | ||||
| This is typically put in place either as a preamp or booster at a ROADM site, or in between two `fiber` `nt::link` elements. | ||||
| No properties are defined. | ||||
|  | ||||
| (yang-topology-fiber)= | ||||
| ### Fiber | ||||
|  | ||||
| An `nt:link` which contains a `fiber` represents a specific, tangible fiber which exists in the physical world. | ||||
| It has a certain length, is made of a particular material, etc. | ||||
| The following properties are defined: | ||||
|  | ||||
| `type` | ||||
|  | ||||
| : Class of the fiber. | ||||
| Refers to the specified fiber material in the equipment library. | ||||
|  | ||||
| `length` | ||||
|  | ||||
| : Total length of the fiber, in :math:`\text{m}`. | ||||
|  | ||||
| `loss-per-km`` | ||||
|  | ||||
| : Fiber attenuation per length. | ||||
| In $\text{dB}/\text{km}$. | ||||
|  | ||||
| `attenuation-in`` | ||||
|  | ||||
| : FIXME: can we remove this and go with a full-blown attenuator instead? | ||||
|  | ||||
| `conn-att-in` and `conn-att-out` | ||||
|  | ||||
| : Attenuation of the input and output connectors, respectively. | ||||
|  | ||||
| #### Raman properties | ||||
|  | ||||
| When using the Raman engine, additional properties are required: | ||||
|  | ||||
| `raman/temperature` | ||||
|  | ||||
| : This is the average temperature of the fiber, given in $\text{K}$. | ||||
|  | ||||
| ### Raman amplification | ||||
|  | ||||
| Actual Raman amplification can be activated by adding several pump lasers below the `raman` container. | ||||
| Use one list member per pump: | ||||
|  | ||||
| `raman/pump[]/frequency` | ||||
|   | ||||
| : Operating frequency of this pump. | ||||
| In $\text{Hz}$. | ||||
|  | ||||
| `raman/pump[]/power` | ||||
|    | ||||
| : Pumping power, in $\text{dBm}$. | ||||
|  | ||||
| `raman/pump[]/direction` | ||||
|  | ||||
| : Direction in which the pumping power is being delivered into the fiber. | ||||
| One of `co-propagating` (pumping in the same direction as the signal), or `counter-propagating` (pumping at the fiber end). | ||||
|  | ||||
| (yang-topology-patch)= | ||||
| ### Patch cords | ||||
|  | ||||
| An `nt:link` with a `patch` element inside corresponds to a short, direct link. | ||||
| Typically, this is used for direct connections between equipment. | ||||
| No non-linearities are considered. | ||||
|  | ||||
| (yang-topology-tentative-link)= | ||||
| ### Tentative links | ||||
|  | ||||
| An `nt:link` which contains a `tentative-link` is a placeholder for a link that will be constructed by GNPy. | ||||
| Unlike either `patch` or `fiber`, this type of a link will never be used in a finalized, fully specified topology. | ||||
|  | ||||
| `type` | ||||
|    | ||||
| : Class of the fiber. | ||||
| Refers to the specified fiber material in the equipment library. | ||||
|  | ||||
| `length` | ||||
|  | ||||
| : Total length of the fiber, in $\text{km}$. | ||||
| @@ -107,6 +107,35 @@ def db2lin(value): | ||||
|  | ||||
|  | ||||
| def round2float(number, step): | ||||
|     """Round a floating point number so that its "resolution" is not bigger than 'step' | ||||
|  | ||||
|     The finest step is fixed at 0.01; smaller values are silently changed to 0.01. | ||||
|  | ||||
|     >>> round2float(123.456, 1000) | ||||
|     0.0 | ||||
|     >>> round2float(123.456, 100) | ||||
|     100.0 | ||||
|     >>> round2float(123.456, 10) | ||||
|     120.0 | ||||
|     >>> round2float(123.456, 1) | ||||
|     123.0 | ||||
|     >>> round2float(123.456, 0.1) | ||||
|     123.5 | ||||
|     >>> round2float(123.456, 0.01) | ||||
|     123.46 | ||||
|     >>> round2float(123.456, 0.001) | ||||
|     123.46 | ||||
|     >>> round2float(123.249, 0.5) | ||||
|     123.0 | ||||
|     >>> round2float(123.250, 0.5) | ||||
|     123.0 | ||||
|     >>> round2float(123.251, 0.5) | ||||
|     123.5 | ||||
|     >>> round2float(123.300, 0.2) | ||||
|     123.2 | ||||
|     >>> round2float(123.301, 0.2) | ||||
|     123.4 | ||||
|     """ | ||||
|     step = round(step, 1) | ||||
|     if step >= 0.01: | ||||
|         number = round(number / step, 0) | ||||
|   | ||||
							
								
								
									
										87
									
								
								gnpy/example-data/2021-demo/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								gnpy/example-data/2021-demo/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| # The GNPy YANG demo at OFC 2021 | ||||
|  | ||||
| The demo needs one piece of YANG-formatted data which includes all settings for GNPy as well as the ONOS topology. | ||||
| This is generated via: | ||||
| ```console-session | ||||
| $ python gnpy/example-data/2021-demo/generate-demo.py | ||||
| ``` | ||||
| ...which puts files into `gnpy/example-data/2021-demo/`. | ||||
|  | ||||
| ```console-session | ||||
| $ FLASK_APP=gnpy.tools.rest_server.app flask run | ||||
| $ curl -v -X POST -H "Content-Type: application/json" -d @gnpy/example-data/2021-demo/yang.json http://localhost:5000/gnpy-experimental/topology | ||||
| ``` | ||||
|  | ||||
| ONOS-formatted `devices.json` and `links.json` are available from the topology: | ||||
|  | ||||
| - `http://localhost:5000/gnpy-experimental/onos/devices` | ||||
| - `http://localhost:5000/gnpy-experimental/onos/links` | ||||
|  | ||||
| ## Misc notes | ||||
|  | ||||
| The version of ONOS I used cannot configure the TX power on our transponders: | ||||
|  | ||||
| ``` | ||||
| 19:06:08.347 INFO [GnpyManager] Configuring egress with power 0.0 for DefaultDevice{id=netconf:10.0.254.103:830, type=TERMINAL_DEVICE, manufacturer=Infinera, hwVersion=Groove, swVersion=4.0.3, serialNumber=, driver=groove} | ||||
| 19:06:08.348 INFO [TerminalDevicePowerConfig] Setting power <rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><edit-config><target><running/></target><config><components xmlns="http://openconfig.net/yang/platform"><component><name>OCH-1-1-L1</name><optical-channel xmlns="http://openconfig.net/yang/terminal-device"><config><target-output-power>0.0</target-output-power></config></optical-channel></component></components></config></edit-config></rpc> | ||||
| 19:06:08.349 DEBUG [TerminalDevicePowerConfig] Request <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> | ||||
|     <edit-config> | ||||
|         <target> | ||||
|             <running/> | ||||
|         </target> | ||||
|         <config> | ||||
|             <components xmlns="http://openconfig.net/yang/platform"> | ||||
|                 <component> | ||||
|                     <name>OCH-1-1-L1</name> | ||||
|                     <optical-channel xmlns="http://openconfig.net/yang/terminal-device"> | ||||
|                         <config> | ||||
|                             <target-output-power>0.0</target-output-power> | ||||
|                         </config> | ||||
|                     </optical-channel> | ||||
|                 </component> | ||||
|             </components> | ||||
|         </config> | ||||
|     </edit-config> | ||||
| </rpc> | ||||
|  | ||||
| 19:06:08.701 DEBUG [TerminalDevicePowerConfig] Response <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="18"> | ||||
|     <ok/> | ||||
| </rpc-reply> | ||||
|  | ||||
| 19:06:08.705 WARN [NetconfSessionMinaImpl] Device netconf:administrator@10.0.254.103:830 has error in reply <?xml version="1.0" encoding="UTF-8"?> | ||||
| <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="18"> | ||||
|   <rpc-error> | ||||
|     <error-type>application</error-type> | ||||
|     <error-tag>operation-not-supported</error-tag> | ||||
|     <error-severity>error</error-severity> | ||||
|     <error-message>Request could not be completed because the requested operation is not supported by this implementation.</error-message> | ||||
|   </rpc-error> | ||||
| </rpc-reply> | ||||
| 19:06:08.706 ERROR [TerminalDevicePowerConfig] error committing channel power | ||||
| org.onosproject.netconf.NetconfException: Request not successful with device netconf:administrator@10.0.254.103:830 with reply <?xml version="1.0" encoding="UTF-8"?> | ||||
| <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="18"> | ||||
|   <rpc-error> | ||||
|     <error-type>application</error-type> | ||||
|     <error-tag>operation-not-supported</error-tag> | ||||
|     <error-severity>error</error-severity> | ||||
|     <error-message>Request could not be completed because the requested operation is not supported by this implementation.</error-message> | ||||
|   </rpc-error> | ||||
| </rpc-reply> | ||||
|         at org.onosproject.netconf.ctl.impl.NetconfSessionMinaImpl.requestSync(NetconfSessionMinaImpl.java:516) ~[?:?] | ||||
|         at org.onosproject.netconf.ctl.impl.NetconfSessionMinaImpl.requestSync(NetconfSessionMinaImpl.java:509) ~[?:?] | ||||
|         at org.onosproject.netconf.AbstractNetconfSession.commit(AbstractNetconfSession.java:336) ~[?:?] | ||||
|         at org.onosproject.drivers.odtn.openconfig.TerminalDevicePowerConfig$ComponentType.setTargetPower(TerminalDevicePowerConfig.java:401) ~[?:?] | ||||
|         at org.onosproject.drivers.odtn.openconfig.TerminalDevicePowerConfig$ComponentType$1.setTargetPower(TerminalDevicePowerConfig.java:315) ~[?:?] | ||||
|         at org.onosproject.drivers.odtn.openconfig.TerminalDevicePowerConfig.setTargetPower(TerminalDevicePowerConfig.java:222) ~[?:?] | ||||
|         at org.onosproject.odtn.impl.GnpyManager.setPathPower(GnpyManager.java:562) ~[?:?] | ||||
|         at org.onosproject.odtn.impl.GnpyManager$InternalIntentListener.lambda$event$0(GnpyManager.java:509) ~[?:?] | ||||
|         at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736) [?:?] | ||||
|         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?] | ||||
|         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?] | ||||
|         at java.lang.Thread.run(Thread.java:834) [?:?] | ||||
| ``` | ||||
|  | ||||
| Filter out launch power settings. | ||||
| It's not needed anyway, the very first node after a transponder is a ROADM. | ||||
							
								
								
									
										11
									
								
								gnpy/example-data/2021-demo/default_edfa_config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gnpy/example-data/2021-demo/default_edfa_config.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| { | ||||
|     "nf_ripple": [ | ||||
|         0.0 | ||||
|     ], | ||||
|     "gain_ripple": [ | ||||
|         0.0 | ||||
|     ], | ||||
|     "dgt": [ | ||||
|         1.0 | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										116
									
								
								gnpy/example-data/2021-demo/equipment.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								gnpy/example-data/2021-demo/equipment.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| {     "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, | ||||
| 	    "pmd_coef": 1.265e-15 | ||||
|             } | ||||
|       ], | ||||
|       "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, | ||||
| 	    "pmd": 0, | ||||
|             "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 | ||||
|                        } | ||||
|                    ] | ||||
|             } | ||||
|       ] | ||||
|  | ||||
| } | ||||
							
								
								
									
										291
									
								
								gnpy/example-data/2021-demo/expected-reply.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								gnpy/example-data/2021-demo/expected-reply.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,291 @@ | ||||
| { | ||||
|   "result": { | ||||
|     "response": [ | ||||
|       { | ||||
|         "path-properties": { | ||||
|           "path-metric": [ | ||||
|             { | ||||
|               "accumulative-value": 19.38, | ||||
|               "metric-type": "SNR-bandwidth" | ||||
|             }, | ||||
|             { | ||||
|               "accumulative-value": 23.46, | ||||
|               "metric-type": "SNR-0.1nm" | ||||
|             }, | ||||
|             { | ||||
|               "accumulative-value": 19.38, | ||||
|               "metric-type": "OSNR-bandwidth" | ||||
|             }, | ||||
|             { | ||||
|               "accumulative-value": 23.47, | ||||
|               "metric-type": "OSNR-0.1nm" | ||||
|             }, | ||||
|             { | ||||
|               "accumulative-value": 100000000000, | ||||
|               "metric-type": "path_bandwidth" | ||||
|             } | ||||
|           ], | ||||
|           "path-route-objects": [ | ||||
|             { | ||||
|               "path-route-object": { | ||||
|                 "index": 0, | ||||
|                 "label-hop": { | ||||
|                   "M": 4, | ||||
|                   "N": -236 | ||||
|                 }, | ||||
|                 "num-unnum-hop": { | ||||
|                   "gnpy-node-type": "transceiver", | ||||
|                   "gnpy-nodes": [ | ||||
|                     "trx-Bremen" | ||||
|                   ], | ||||
|                   "link-tp-id": "netconf:10.0.254.103:830", | ||||
|                   "node-id": "netconf:10.0.254.103:830", | ||||
|                   "transponder": { | ||||
|                     "transponder-mode": "dp-qpsk", | ||||
|                     "transponder-type": "Cassini" | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             }, | ||||
|             { | ||||
|               "path-route-object": { | ||||
|                 "index": 1, | ||||
|                 "label-hop": { | ||||
|                   "M": 4, | ||||
|                   "N": -236 | ||||
|                 }, | ||||
|                 "num-unnum-hop": { | ||||
|                   "gnpy-node-type": "ROADM", | ||||
|                   "gnpy-nodes": [ | ||||
|                     "roadm-Bremen-AD" | ||||
|                   ], | ||||
|                   "link-tp-id": "netconf:10.0.254.225:830", | ||||
|                   "node-id": "netconf:10.0.254.225:830", | ||||
|                   "target-channel-power": -12 | ||||
|                 } | ||||
|               } | ||||
|             }, | ||||
|             { | ||||
|               "path-route-object": { | ||||
|                 "index": 5, | ||||
|                 "label-hop": { | ||||
|                   "M": 4, | ||||
|                   "N": -236 | ||||
|                 }, | ||||
|                 "num-unnum-hop": { | ||||
|                   "gnpy-node-type": "ROADM", | ||||
|                   "gnpy-nodes": [ | ||||
|                     "roadm-Bremen-L2" | ||||
|                   ], | ||||
|                   "link-tp-id": "netconf:10.0.254.102:830", | ||||
|                   "node-id": "netconf:10.0.254.102:830", | ||||
|                   "target-channel-power": -23 | ||||
|                 } | ||||
|               } | ||||
|             }, | ||||
|             { | ||||
|               "path-route-object": { | ||||
|                 "index": 9, | ||||
|                 "label-hop": { | ||||
|                   "M": 4, | ||||
|                   "N": -236 | ||||
|                 }, | ||||
|                 "num-unnum-hop": { | ||||
|                   "gnpy-node-type": "ROADM", | ||||
|                   "gnpy-nodes": [ | ||||
|                     "roadm-Amsterdam-L1" | ||||
|                   ], | ||||
|                   "link-tp-id": "netconf:10.0.254.78:830", | ||||
|                   "node-id": "netconf:10.0.254.78:830", | ||||
|                   "target-channel-power": -12 | ||||
|                 } | ||||
|               } | ||||
|             }, | ||||
|             { | ||||
|               "path-route-object": { | ||||
|                 "index": 13, | ||||
|                 "label-hop": { | ||||
|                   "M": 4, | ||||
|                   "N": -236 | ||||
|                 }, | ||||
|                 "num-unnum-hop": { | ||||
|                   "gnpy-node-type": "ROADM", | ||||
|                   "gnpy-nodes": [ | ||||
|                     "roadm-Amsterdam-AD" | ||||
|                   ], | ||||
|                   "link-tp-id": "netconf:10.0.254.107:830", | ||||
|                   "node-id": "netconf:10.0.254.107:830", | ||||
|                   "target-channel-power": -13 | ||||
|                 } | ||||
|               } | ||||
|             }, | ||||
|             { | ||||
|               "path-route-object": { | ||||
|                 "index": 14, | ||||
|                 "label-hop": { | ||||
|                   "M": 4, | ||||
|                   "N": -236 | ||||
|                 }, | ||||
|                 "num-unnum-hop": { | ||||
|                   "gnpy-node-type": "transceiver", | ||||
|                   "gnpy-nodes": [ | ||||
|                     "trx-Amsterdam" | ||||
|                   ], | ||||
|                   "link-tp-id": "netconf:10.0.254.105:830", | ||||
|                   "node-id": "netconf:10.0.254.105:830", | ||||
|                   "transponder": { | ||||
|                     "transponder-mode": "dp-qpsk", | ||||
|                     "transponder-type": "Cassini" | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           ], | ||||
|           "reversed-path-route-objects": [ | ||||
|             { | ||||
|               "path-route-object": { | ||||
|                 "index": 0, | ||||
|                 "label-hop": { | ||||
|                   "M": 4, | ||||
|                   "N": -236 | ||||
|                 }, | ||||
|                 "num-unnum-hop": { | ||||
|                   "gnpy-node-type": "transceiver", | ||||
|                   "gnpy-nodes": [ | ||||
|                     "trx-Amsterdam" | ||||
|                   ], | ||||
|                   "link-tp-id": "netconf:10.0.254.105:830", | ||||
|                   "node-id": "netconf:10.0.254.105:830", | ||||
|                   "transponder": { | ||||
|                     "transponder-mode": "dp-qpsk", | ||||
|                     "transponder-type": "Cassini" | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             }, | ||||
|             { | ||||
|               "path-route-object": { | ||||
|                 "index": 1, | ||||
|                 "label-hop": { | ||||
|                   "M": 4, | ||||
|                   "N": -236 | ||||
|                 }, | ||||
|                 "num-unnum-hop": { | ||||
|                   "gnpy-node-type": "ROADM", | ||||
|                   "gnpy-nodes": [ | ||||
|                     "roadm-Amsterdam-AD" | ||||
|                   ], | ||||
|                   "link-tp-id": "netconf:10.0.254.107:830", | ||||
|                   "node-id": "netconf:10.0.254.107:830", | ||||
|                   "target-channel-power": -12 | ||||
|                 } | ||||
|               } | ||||
|             }, | ||||
|             { | ||||
|               "path-route-object": { | ||||
|                 "index": 6, | ||||
|                 "label-hop": { | ||||
|                   "M": 4, | ||||
|                   "N": -236 | ||||
|                 }, | ||||
|                 "num-unnum-hop": { | ||||
|                   "gnpy-node-type": "EDFA", | ||||
|                   "gnpy-nodes": [ | ||||
|                     "roadm-Amsterdam-L1-booster" | ||||
|                   ], | ||||
|                   "link-tp-id": "netconf:10.0.254.78:830", | ||||
|                   "node-id": "netconf:10.0.254.78:830", | ||||
|                   "target-channel-power": -1 | ||||
|                 } | ||||
|               } | ||||
|             }, | ||||
|             { | ||||
|               "path-route-object": { | ||||
|                 "index": 9, | ||||
|                 "label-hop": { | ||||
|                   "M": 4, | ||||
|                   "N": -236 | ||||
|                 }, | ||||
|                 "num-unnum-hop": { | ||||
|                   "gnpy-node-type": "ROADM", | ||||
|                   "gnpy-nodes": [ | ||||
|                     "roadm-Bremen-L2" | ||||
|                   ], | ||||
|                   "link-tp-id": "netconf:10.0.254.102:830", | ||||
|                   "node-id": "netconf:10.0.254.102:830", | ||||
|                   "target-channel-power": -12 | ||||
|                 } | ||||
|               } | ||||
|             }, | ||||
|             { | ||||
|               "path-route-object": { | ||||
|                 "index": 13, | ||||
|                 "label-hop": { | ||||
|                   "M": 4, | ||||
|                   "N": -236 | ||||
|                 }, | ||||
|                 "num-unnum-hop": { | ||||
|                   "gnpy-node-type": "ROADM", | ||||
|                   "gnpy-nodes": [ | ||||
|                     "roadm-Bremen-AD" | ||||
|                   ], | ||||
|                   "link-tp-id": "netconf:10.0.254.225:830", | ||||
|                   "node-id": "netconf:10.0.254.225:830", | ||||
|                   "target-channel-power": -13 | ||||
|                 } | ||||
|               } | ||||
|             }, | ||||
|             { | ||||
|               "path-route-object": { | ||||
|                 "index": 14, | ||||
|                 "label-hop": { | ||||
|                   "M": 4, | ||||
|                   "N": -236 | ||||
|                 }, | ||||
|                 "num-unnum-hop": { | ||||
|                   "gnpy-node-type": "transceiver", | ||||
|                   "gnpy-nodes": [ | ||||
|                     "trx-Bremen" | ||||
|                   ], | ||||
|                   "link-tp-id": "netconf:10.0.254.103:830", | ||||
|                   "node-id": "netconf:10.0.254.103:830", | ||||
|                   "transponder": { | ||||
|                     "transponder-mode": "dp-qpsk", | ||||
|                     "transponder-type": "Cassini" | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           ], | ||||
|           "z-a-path-metric": [ | ||||
|             { | ||||
|               "accumulative-value": 19.38, | ||||
|               "metric-type": "SNR-bandwidth" | ||||
|             }, | ||||
|             { | ||||
|               "accumulative-value": 23.46, | ||||
|               "metric-type": "SNR-0.1nm" | ||||
|             }, | ||||
|             { | ||||
|               "accumulative-value": 19.38, | ||||
|               "metric-type": "OSNR-bandwidth" | ||||
|             }, | ||||
|             { | ||||
|               "accumulative-value": 23.47, | ||||
|               "metric-type": "OSNR-0.1nm" | ||||
|             }, | ||||
|             { | ||||
|               "accumulative-value": 0.001, | ||||
|               "metric-type": "reference_power" | ||||
|             }, | ||||
|             { | ||||
|               "accumulative-value": 100000000000, | ||||
|               "metric-type": "path_bandwidth" | ||||
|             } | ||||
|           ] | ||||
|         }, | ||||
|         "response-id": "onos-3" | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
| } | ||||
							
								
								
									
										447
									
								
								gnpy/example-data/2021-demo/generate-demo.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										447
									
								
								gnpy/example-data/2021-demo/generate-demo.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,447 @@ | ||||
| import json | ||||
| from pathlib import Path | ||||
| from gnpy.tools.json_io import load_equipment, load_network | ||||
| from gnpy.yang.io import save_to_json | ||||
|  | ||||
| # 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_variety": "Cassini", "type": "Transceiver"} | ||||
|     ) | ||||
|     target_pwr = { | ||||
|         f"trx-{CITY}": -8, | ||||
|         f"splice-(roadm-{CITY}-AD)-(patch-(roadm-{CITY}-AD)-(roadm-{CITY}-L1))": -12, | ||||
|         f"splice-(roadm-{CITY}-AD)-(patch-(roadm-{CITY}-AD)-(roadm-{CITY}-L2))": -12, | ||||
|     } | ||||
|     J["elements"].append( | ||||
|         {"uid": f"roadm-{CITY}-AD", "type": "Roadm", "params": {"target_pch_out_db": -2.0, "per_degree_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 = { | ||||
|             f"roadm-{CITY}-L{n}-booster": -23, | ||||
|             f"splice-(roadm-{CITY}-L{n})-(patch-(roadm-{CITY}-L{n})-(roadm-{CITY}-AD))": -12, | ||||
|         } | ||||
|         for m in (1,2): | ||||
|             if m == n: | ||||
|                 continue | ||||
|             target_pwr[f"splice-(roadm-{CITY}-L{n})-(patch-(roadm-{CITY}-L{n})-(roadm-{CITY}-L{m}))"] = -12 | ||||
|         J["elements"].append( | ||||
|             {"uid": f"roadm-{CITY}-L{n}", "type": "Roadm", "params": {"target_pch_out_db": -23.0, "per_degree_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 | ||||
|             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 | ||||
|  | ||||
| with open('gnpy/example-data/2021-demo/original-gnpy.json', 'w') as f: | ||||
|     json.dump(J, f, indent=2) | ||||
|  | ||||
| equipment = load_equipment('gnpy/example-data/2021-demo/equipment.json') | ||||
| network = load_network(Path('gnpy/example-data/2021-demo/original-gnpy.json'), equipment) | ||||
| yang_bundle = save_to_json(equipment, network) | ||||
| with open('gnpy/example-data/2021-demo/yang-without-onos.json', 'w') as f: | ||||
|     json.dump(yang_bundle, f, indent=2) | ||||
| yang_bundle['ietf-network:networks']['network'].append({ | ||||
|     "network-id": "ONOS", | ||||
|     "network-types": { | ||||
|       "tip-onos-topology:onos-topology": { | ||||
|       } | ||||
|     }, | ||||
|     "node": [ | ||||
|       { | ||||
|         "node-id": "netconf:10.0.254.105:830", | ||||
|         "supporting-node": [ | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "trx-Amsterdam" | ||||
|           } | ||||
|         ], | ||||
|         "tip-onos-topology:device": { | ||||
|           "name": "Amsterdam TXP (g30-horni)", | ||||
|           "driver": "groove", | ||||
|           "grid-x": -150, | ||||
|           "grid-y": 350, | ||||
|           "netconf": { | ||||
|             "username": "administrator", | ||||
|             "password": "e2e!Net4u#" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       { | ||||
|         "node-id": "netconf:10.0.254.78:830", | ||||
|         "supporting-node": [ | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Amsterdam-L1" | ||||
|           }, | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Amsterdam-L1-preamp" | ||||
|           }, | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Amsterdam-L1-booster" | ||||
|           } | ||||
|         ], | ||||
|         "tip-onos-topology:device": { | ||||
|           "name": "Amsterdam L1 to Bremen (line-QR79)", | ||||
|           "driver": "czechlight-roadm", | ||||
|           "grid-x": 225, | ||||
|           "grid-y": 320, | ||||
|           "netconf": { | ||||
|             "idle-timeout": 0, | ||||
|             "username": "dwdm", | ||||
|             "password": "dwdm" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       { | ||||
|         "node-id": "netconf:10.0.254.79:830", | ||||
|         "supporting-node": [ | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Amsterdam-L2" | ||||
|           }, | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Amsterdam-L2-boster" | ||||
|           }, | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Amsterdam-L2-preamp" | ||||
|           } | ||||
|         ], | ||||
|         "tip-onos-topology:device": { | ||||
|           "name": "Amsterdam L2 to Cologne (line-Q7JS)", | ||||
|           "driver": "czechlight-roadm", | ||||
|           "grid-x": 225, | ||||
|           "grid-y": 380, | ||||
|           "netconf": { | ||||
|             "idle-timeout": 0, | ||||
|             "username": "dwdm", | ||||
|             "password": "dwdm" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       { | ||||
|         "node-id": "netconf:10.0.254.107:830", | ||||
|         "supporting-node": [ | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Amsterdam-AD" | ||||
|           } | ||||
|         ], | ||||
|         "tip-onos-topology:device": { | ||||
|           "name": "Amsterdam Add/Drop (coh-a-d-v9u)", | ||||
|           "driver": "czechlight-roadm", | ||||
|           "grid-x": 175, | ||||
|           "grid-y": 350, | ||||
|           "netconf": { | ||||
|             "idle-timeout": 0, | ||||
|             "username": "dwdm", | ||||
|             "password": "dwdm" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       { | ||||
|         "node-id": "netconf:10.0.254.99:830", | ||||
|         "supporting-node": [ | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Cologne-L1" | ||||
|           }, | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Cologne-L1-preamp" | ||||
|           }, | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Cologne-L1-booster" | ||||
|           } | ||||
|         ], | ||||
|         "tip-onos-topology:device": { | ||||
|           "name": "Cologne L1 to Amsterdam (line-TQQ)", | ||||
|           "driver": "czechlight-roadm", | ||||
|           "grid-x": 420, | ||||
|           "grid-y": 550, | ||||
|           "netconf": { | ||||
|             "idle-timeout": 0, | ||||
|             "username": "dwdm", | ||||
|             "password": "dwdm" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       { | ||||
|         "node-id": "netconf:10.0.254.104:830", | ||||
|         "supporting-node": [ | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Cologne-L2" | ||||
|           }, | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Cologne-L2-boster" | ||||
|           }, | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Cologne-L2-preamp" | ||||
|           } | ||||
|         ], | ||||
|         "tip-onos-topology:device": { | ||||
|           "name": "Cologne L2 to Bremen (line-QLK6)", | ||||
|           "driver": "czechlight-roadm", | ||||
|           "grid-x": 480, | ||||
|           "grid-y": 550, | ||||
|           "netconf": { | ||||
|             "idle-timeout": 0, | ||||
|             "username": "dwdm", | ||||
|             "password": "dwdm" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       { | ||||
|         "node-id": "netconf:10.0.254.100:830", | ||||
|         "supporting-node": [ | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Bremen-L1" | ||||
|           }, | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Bremen-L1-preamp" | ||||
|           }, | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Bremen-L1-booster" | ||||
|           } | ||||
|         ], | ||||
|         "tip-onos-topology:device": { | ||||
|           "name": "Bremen L1 to Cologne (line-WKP)", | ||||
|           "driver": "czechlight-roadm", | ||||
|           "grid-x": 700, | ||||
|           "grid-y": 380, | ||||
|           "netconf": { | ||||
|             "idle-timeout": 0, | ||||
|             "username": "dwdm", | ||||
|             "password": "dwdm" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       { | ||||
|         "node-id": "netconf:10.0.254.102:830", | ||||
|         "supporting-node": [ | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Bremen-L2" | ||||
|           }, | ||||
|           # try removing the following section to see how a wrong power config affects the results | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Bremen-L2-booster" | ||||
|           }, | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Bremen-L2-preamp" | ||||
|           } | ||||
|         ], | ||||
|         "tip-onos-topology:device": { | ||||
|           "name": "Bremen L2 to Amsterdam (line-QCP9)", | ||||
|           "driver": "czechlight-roadm", | ||||
|           "grid-x": 700, | ||||
|           "grid-y": 320, | ||||
|           "netconf": { | ||||
|             "idle-timeout": 0, | ||||
|             "username": "dwdm", | ||||
|             "password": "dwdm" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       { | ||||
|         "node-id": "netconf:10.0.254.225:830", | ||||
|         "supporting-node": [ | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "roadm-Bremen-AD" | ||||
|           } | ||||
|         ], | ||||
|         "tip-onos-topology:device": { | ||||
|           "name": "Bremen Add/Drop (add-drop-SPI)", | ||||
|           "driver": "czechlight-roadm", | ||||
|           "grid-x": 750, | ||||
|           "grid-y": 350, | ||||
|           "netconf": { | ||||
|             "idle-timeout": 0, | ||||
|             "username": "dwdm", | ||||
|             "password": "dwdm" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       { | ||||
|         "node-id": "netconf:10.0.254.103:830", | ||||
|         "supporting-node": [ | ||||
|           { | ||||
|             "network-ref": "GNPy", | ||||
|             "node-ref": "trx-Bremen" | ||||
|           } | ||||
|         ], | ||||
|         "tip-onos-topology:device": { | ||||
|           "name": "Amsterdam TXP (g30-spodni)", | ||||
|           "driver": "groove", | ||||
|           "grid-x": 1050, | ||||
|           "grid-y": 350, | ||||
|           "netconf": { | ||||
|             "username": "administrator", | ||||
|             "password": "e2e!Net4u#" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     ], | ||||
|     "ietf-network-topology:link": [ | ||||
|       { | ||||
|         "link-id": "netconf:10.0.254.105:830/10101-netconf:10.0.254.107:830/1" | ||||
|       }, | ||||
|       { | ||||
|         "link-id": "netconf:10.0.254.107:830/100-netconf:10.0.254.78:830/1" | ||||
|       }, | ||||
|       { | ||||
|         "link-id": "netconf:10.0.254.107:830/100-netconf:10.0.254.79:830/2" | ||||
|       }, | ||||
|       { | ||||
|         "link-id": "netconf:10.0.254.79:830/1-netconf:10.0.254.78:830/2" | ||||
|       }, | ||||
|       { | ||||
|         "link-id": "netconf:10.0.254.99:830/1-netconf:10.0.254.104:830/1" | ||||
|       }, | ||||
|       { | ||||
|         "link-id": "netconf:10.0.254.79:830/100-netconf:10.0.254.99:830/100" | ||||
|       }, | ||||
|       { | ||||
|         "link-id": "netconf:10.0.254.104:830/100-netconf:10.0.254.100:830/100" | ||||
|       }, | ||||
|       { | ||||
|         "link-id": "netconf:10.0.254.102:830/100-netconf:10.0.254.78:830/100" | ||||
|       }, | ||||
|       { | ||||
|         "link-id": "netconf:10.0.254.100:830/1-netconf:10.0.254.225:830/100" | ||||
|       }, | ||||
|       { | ||||
|         "link-id": "netconf:10.0.254.102:830/2-netconf:10.0.254.225:830/100" | ||||
|       }, | ||||
|       { | ||||
|         "link-id": "netconf:10.0.254.102:830/1-netconf:10.0.254.100:830/2" | ||||
|       }, | ||||
|       { | ||||
|         "link-id": "netconf:10.0.254.103:830/10101-netconf:10.0.254.225:830/1" | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
| ) | ||||
| with open('gnpy/example-data/2021-demo/yang.json', 'w') as f: | ||||
|     json.dump(yang_bundle, f, indent=2) | ||||
							
								
								
									
										1
									
								
								gnpy/example-data/2021-demo/onos-real-request.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								gnpy/example-data/2021-demo/onos-real-request.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| {"path-request":[{"request-id":"onos-3","source":"netconf:10.0.254.103:830","destination":"netconf:10.0.254.105:830","src-tp-id":"netconf:10.0.254.103:830","dst-tp-id":"netconf:10.0.254.105:830","bidirectional":true,"path-constraints":{"te-bandwidth":{"technology":"flexi-grid","trx_type":"Cassini","trx_mode":null,"effective-freq-slot":[{"N":"null","M":"null"}],"spacing":5.0E10,"max-nb-of-channel":null,"output-power":null,"path_bandwidth":1.0E11}}}]} | ||||
							
								
								
									
										1185
									
								
								gnpy/example-data/2021-demo/original-gnpy.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1185
									
								
								gnpy/example-data/2021-demo/original-gnpy.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1518
									
								
								gnpy/example-data/2021-demo/yang-without-onos.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1518
									
								
								gnpy/example-data/2021-demo/yang-without-onos.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1810
									
								
								gnpy/example-data/2021-demo/yang.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1810
									
								
								gnpy/example-data/2021-demo/yang.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -138,7 +138,7 @@ | ||||
|                        "baud_rate": 27.95e9, | ||||
|                        "OSNR": 17, | ||||
|                        "bit_rate": 100e9, | ||||
|                        "roll_off": null, | ||||
|                        "roll_off": 0.15, | ||||
|                        "tx_osnr": 33, | ||||
|                        "min_spacing": 50e9, | ||||
|                        "cost":1 | ||||
|   | ||||
| @@ -9,6 +9,7 @@ Common code for CLI examples | ||||
| ''' | ||||
|  | ||||
| import argparse | ||||
| import json | ||||
| import logging | ||||
| import os.path | ||||
| import sys | ||||
| @@ -31,6 +32,7 @@ from gnpy.topology.spectrum_assignment import build_oms_list, pth_assign_spectru | ||||
| from gnpy.tools.json_io import load_equipment, load_network, load_json, load_requests, save_network, \ | ||||
|                                requests_from_json, disjunctions_from_json, save_json | ||||
| from gnpy.tools.plots import plot_baseline, plot_results | ||||
| from gnpy.yang.io import load_from_yang, save_to_json | ||||
|  | ||||
| _logger = logging.getLogger(__name__) | ||||
| _examples_dir = Path(__file__).parent.parent / 'example-data' | ||||
| @@ -42,22 +44,28 @@ Learn more at https://gnpy.readthedocs.io/ | ||||
| ''' | ||||
| _help_fname_json = 'FILE.json' | ||||
| _help_fname_json_csv = 'FILE.(json|csv)' | ||||
| _help_fname_yangjson = 'FILE-with-YANG.json' | ||||
|  | ||||
|  | ||||
| def show_example_data_dir(): | ||||
|     print(f'{_examples_dir}/') | ||||
|  | ||||
|  | ||||
| def _load_network_legacy(topology_filename, equipment, save_raw_network_filename): | ||||
|     network = load_network(topology_filename, equipment) | ||||
|     if save_raw_network_filename is not None: | ||||
|         save_network(network, save_raw_network_filename) | ||||
|         print(f'{ansi_escapes.blue}Raw network (no optimizations) saved to {save_raw_network_filename}{ansi_escapes.reset}') | ||||
|     return network | ||||
|  | ||||
|  | ||||
| def load_common_data(equipment_filename, topology_filename, simulation_filename, save_raw_network_filename): | ||||
|     '''Load common configuration from JSON files''' | ||||
|  | ||||
|     try: | ||||
|         equipment = load_equipment(equipment_filename) | ||||
|         network = load_network(topology_filename, equipment) | ||||
|         if save_raw_network_filename is not None: | ||||
|             save_network(network, save_raw_network_filename) | ||||
|             print(f'{ansi_escapes.blue}Raw network (no optimizations) saved to {save_raw_network_filename}{ansi_escapes.reset}') | ||||
|         sim_params = SimParams(**load_json(simulation_filename)) if simulation_filename is not None else None | ||||
|         network = _load_network_legacy(topology_filename, equipment, save_raw_network_filename) | ||||
|         if not sim_params: | ||||
|             if next((node for node in network if isinstance(node, RamanFiber)), None) is not None: | ||||
|                 print(f'{ansi_escapes.red}Invocation error:{ansi_escapes.reset} ' | ||||
| @@ -88,14 +96,17 @@ def _setup_logging(args): | ||||
|     logging.basicConfig(level={2: logging.DEBUG, 1: logging.INFO, 0: logging.CRITICAL}.get(args.verbose, logging.DEBUG)) | ||||
|  | ||||
|  | ||||
| def _parser_add_equipment(parser: argparse.ArgumentParser): | ||||
|     parser.add_argument('-e', '--equipment', type=Path, metavar=_help_fname_json, | ||||
|                         default=_examples_dir / 'eqpt_config.json', help='Equipment library') | ||||
|  | ||||
| def _add_common_options(parser: argparse.ArgumentParser, network_default: Path): | ||||
|     parser.add_argument('topology', nargs='?', type=Path, metavar='NETWORK-TOPOLOGY.(json|xls|xlsx)', | ||||
|                         default=network_default, | ||||
|                         help='Input network topology') | ||||
|     parser.add_argument('-v', '--verbose', action='count', default=0, | ||||
|                         help='Increase verbosity (can be specified several times)') | ||||
|     parser.add_argument('-e', '--equipment', type=Path, metavar=_help_fname_json, | ||||
|                         default=_examples_dir / 'eqpt_config.json', help='Equipment library') | ||||
|     _parser_add_equipment(parser) | ||||
|     parser.add_argument('--sim-params', type=Path, metavar=_help_fname_json, | ||||
|                         default=None, help='Path to the JSON containing simulation parameters (required for Raman). ' | ||||
|                                            f'Example: {_examples_dir / "sim_params.json"}') | ||||
| @@ -103,6 +114,8 @@ def _add_common_options(parser: argparse.ArgumentParser, network_default: Path): | ||||
|                         help='Save the final network as a JSON file') | ||||
|     parser.add_argument('--save-network-before-autodesign', type=Path, metavar=_help_fname_json, | ||||
|                         help='Dump the network into a JSON file prior to autodesign') | ||||
|     parser.add_argument('--from-yang', type=Path, metavar=_help_fname_yangjson, | ||||
|                         help='Load equipment, (in future also topology) and simulation parameters from a YANG-formatted JSON file') | ||||
|  | ||||
|  | ||||
| def transmission_main_example(args=None): | ||||
| @@ -122,7 +135,15 @@ def transmission_main_example(args=None): | ||||
|     args = parser.parse_args(args if args is not None else sys.argv[1:]) | ||||
|     _setup_logging(args) | ||||
|  | ||||
|     (equipment, network) = load_common_data(args.equipment, args.topology, args.sim_params, args.save_network_before_autodesign) | ||||
|     if args.from_yang: | ||||
|         # FIXME: move this into a better place, it does not belong to a CLI frontend | ||||
|         with open(args.from_yang, 'r') as f: | ||||
|             raw_json = json.load(f) | ||||
|         (equipment, network) = load_from_yang(raw_json) | ||||
|         if args.save_network_before_autodesign is not None: | ||||
|             save_network(network, args.save_network_before_autodesign) | ||||
|     else: | ||||
|         (equipment, network) = load_common_data(args.equipment, args.topology, args.sim_params, args.save_network_before_autodesign) | ||||
|  | ||||
|     if args.plot: | ||||
|         plot_baseline(network) | ||||
| @@ -214,17 +235,16 @@ def transmission_main_example(args=None): | ||||
|           f'and {destination.uid}') | ||||
|     print(f'\nNow propagating between {source.uid} and {destination.uid}:') | ||||
|  | ||||
|     try: | ||||
|         p_start, p_stop, p_step = equipment['SI']['default'].power_range_db | ||||
|         p_num = abs(int(round((p_stop - p_start) / p_step))) + 1 if p_step != 0 else 1 | ||||
|         power_range = list(linspace(p_start, p_stop, p_num)) | ||||
|     except TypeError: | ||||
|         print('invalid power range definition in eqpt_config, should be power_range_db: [lower, upper, step]') | ||||
|         power_range = [0] | ||||
|  | ||||
|     if not power_mode: | ||||
|     power_range = [0] | ||||
|     if power_mode: | ||||
|         # power cannot be changed in gain mode | ||||
|         power_range = [0] | ||||
|         try: | ||||
|             p_start, p_stop, p_step = equipment['SI']['default'].power_range_db | ||||
|             p_num = abs(int(round((p_stop - p_start) / p_step))) + 1 if p_step != 0 else 1 | ||||
|             power_range = list(linspace(p_start, p_stop, p_num)) | ||||
|         except TypeError: | ||||
|             print('invalid power range definition in eqpt_config, should be power_range_db: [lower, upper, step]') | ||||
|  | ||||
|     for dp_db in power_range: | ||||
|         req.power = db2lin(pref_ch_db + dp_db) * 1e-3 | ||||
|         if power_mode: | ||||
| @@ -440,3 +460,22 @@ def path_requests_run(args=None): | ||||
|         else: | ||||
|             print(f'{ansi_escapes.red}Cannot save output: neither JSON nor CSV file{ansi_escapes.reset}') | ||||
|             sys.exit(1) | ||||
|  | ||||
|  | ||||
| def convert_to_yang(args=None): | ||||
|     parser = argparse.ArgumentParser( | ||||
|         description='Convert data to the YANG+JSON data format', | ||||
|         epilog=_help_footer, | ||||
|         formatter_class=argparse.ArgumentDefaultsHelpFormatter, | ||||
|         ) | ||||
|     _parser_add_equipment(parser) | ||||
|     parser.add_argument('--topology', type=Path, metavar='NETWORK-TOPOLOGY.(json|xls|xlsx)', | ||||
|                         help='Input network topology') | ||||
|  | ||||
|     args = parser.parse_args(args if args is not None else sys.argv[1:]) | ||||
|  | ||||
|     equipment = load_equipment(args.equipment) | ||||
|     network = load_network(args.topology, equipment) if args.topology is not None else None | ||||
|  | ||||
|     data = save_to_json(equipment, network) | ||||
|     print(json.dumps(data, indent=2)) | ||||
|   | ||||
							
								
								
									
										198
									
								
								gnpy/tools/rest_server/app.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								gnpy/tools/rest_server/app.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,198 @@ | ||||
| # SPDX-License-Identifier: BSD-3-Clause | ||||
| # | ||||
| # Copyright (C) 2021 Telecom Infra Project and GNPy contributors | ||||
| # see LICENSE.md for a list of contributors | ||||
| # | ||||
|  | ||||
| from gnpy.yang.io import load_from_yang | ||||
| from gnpy.core.network import build_network | ||||
| from gnpy.core.utils import lin2db, automatic_nch | ||||
| from gnpy.topology.request import deduplicate_disjunctions, requests_aggregation, \ | ||||
|     compute_path_dsjctn, compute_path_with_disjunction, ResultElement | ||||
| from gnpy.topology.spectrum_assignment import build_oms_list, pth_assign_spectrum | ||||
| from gnpy.tools.json_io import disjunctions_from_json, requests_from_json | ||||
| from flask import Flask, request, abort, Response | ||||
| import copy | ||||
|  | ||||
|  | ||||
| class YangRunner: | ||||
|     def __init__(self): | ||||
|         self.equipment = None | ||||
|         self.network = None | ||||
|         self.onos_devices = {} | ||||
|         self.onos_links = {} | ||||
|  | ||||
|     mapping = {} | ||||
|  | ||||
|     def parse_onos_network(self, data): | ||||
|         gnpy_network_name = None | ||||
|         for network in data['ietf-network:networks']['network']: | ||||
|             if 'tip-photonic-topology:photonic-topology' in network['network-types']: | ||||
|                 gnpy_network_name = network['network-id'] | ||||
|                 break | ||||
|         if gnpy_network_name is None: | ||||
|             raise Exception('Cannot find that GNPy topology') | ||||
|  | ||||
|         for network in data['ietf-network:networks']['network']: | ||||
|             if 'tip-onos-topology:onos-topology' not in network['network-types']: | ||||
|                 continue | ||||
|             for node in network['node']: | ||||
|                 device_id = node['node-id'] | ||||
|                 proto, ip, port = device_id.split(':')  # no clue about IPv6 | ||||
|                 if port != '830': | ||||
|                     raise Exception(f'Fishy DeviceID in ONOS topology: {device_id}') | ||||
|                 for supporting_node in node['supporting-node']: | ||||
|                     if supporting_node['network-ref'] != gnpy_network_name: | ||||
|                         continue | ||||
|                     self.mapping[supporting_node['node-ref']] = ip | ||||
|                 if 'tip-onos-topology:device' not in node: | ||||
|                     continue | ||||
|                 onos_dev = node['tip-onos-topology:device'] | ||||
|                 dev = { | ||||
|                     'basic': { | ||||
|                         'name': onos_dev['name'], | ||||
|                         'driver': onos_dev['driver'], | ||||
|                         'gridX': onos_dev['grid-x'], | ||||
|                         'gridY': onos_dev['grid-y'], | ||||
|                     }, | ||||
|                     'netconf': { | ||||
|                         'username': onos_dev['netconf']['username'], | ||||
|                         'password': onos_dev['netconf']['password'], | ||||
|                     }, | ||||
|                 } | ||||
|                 if 'idle-timeout' in onos_dev['netconf']: | ||||
|                     dev['netconf']['idle-timeout'] = onos_dev['netconf']['idle-timeout'] | ||||
|                 self.onos_devices[device_id] = dev | ||||
|  | ||||
|             for link in network['ietf-network-topology:link']: | ||||
|                 link_id = link['link-id'] | ||||
|                 a, b = link_id.split('-') | ||||
|                 for device_id in a, b: | ||||
|                     proto, ip, port = device_id.split(':')  # no clue about IPv6 | ||||
|                     if ip not in self.mapping.values(): | ||||
|                         raise Exception(f'Link {link_id} refers to an undefiend device address: {ip}') | ||||
|                     self.onos_links[link_id] = { | ||||
|                         'basic': { | ||||
|                             'type': 'OPTICAL', | ||||
|                             'durable': True, | ||||
|                             'bidirectional': True, | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|     def upload_equipment_and_network(self, data): | ||||
|         self.parse_onos_network(data) | ||||
|         self.equipment, self.network = load_from_yang(data) | ||||
|         p_db = self.equipment['SI']['default'].power_dbm | ||||
|         p_total_db = p_db + lin2db(automatic_nch(self.equipment['SI']['default'].f_min, | ||||
|                                                  self.equipment['SI']['default'].f_max, | ||||
|                                                  self.equipment['SI']['default'].spacing)) | ||||
|         build_network(self.network, self.equipment, p_db, p_total_db) | ||||
|         self.oms_list = build_oms_list(self.network, self.equipment) | ||||
|  | ||||
|     def handle_request(self, incoming): | ||||
|         backup_net = copy.deepcopy(self.network) | ||||
|         backup_oms_list = copy.deepcopy(self.oms_list) | ||||
|         try: | ||||
|             if self.equipment is None or self.network is None: | ||||
|                 raise Exception('Missing equipment library or the network topology') | ||||
|  | ||||
|             requests = requests_from_json(incoming, self.equipment) | ||||
|             disjunctions = disjunctions_from_json(requests) | ||||
|             disjunctions = deduplicate_disjunctions(disjunctions) | ||||
|             requests, disjunctions = requests_aggregation(requests, disjunctions) | ||||
|             paths = compute_path_dsjctn(self.network, self.equipment, requests, disjunctions) | ||||
|             propagated_paths, reversed_paths, reversed_propagated_paths = \ | ||||
|                 compute_path_with_disjunction(self.network, self.equipment, requests, paths) | ||||
|             pth_assign_spectrum(paths, requests, self.oms_list, reversed_paths) | ||||
|  | ||||
|             return [ResultElement(requests[i], path, reversed_propagated_paths[i]).json | ||||
|                     for i, path in enumerate(propagated_paths)] | ||||
|         finally: | ||||
|             self.network = backup_net | ||||
|             self.oms_list = backup_oms_list | ||||
|  | ||||
|     def handle_request_with_translation(self, incoming): | ||||
|         fixed_input = {'path-request': []} | ||||
|         for item in incoming['path-request']: | ||||
|             for k in ('source', 'destination', 'src-tp-id', 'dst-tp-id'): | ||||
|                 item[k] = self.incoming_name_for(item[k]) | ||||
|             fixed_input['path-request'].append(item) | ||||
|  | ||||
|         responses = self.handle_request(fixed_input) | ||||
|         for response in responses: | ||||
|             # Filter out 'reference_power' because ONOS uses that for TXP launch power and that's broken on my TXPs | ||||
|             response['path-properties']['path-metric'] = [ | ||||
|                 metric for metric in response['path-properties']['path-metric'] | ||||
|                 if metric['metric-type'] != 'reference_power' | ||||
|             ] | ||||
|  | ||||
|             # Filter GNPy-level NEs which do not apply to ONOS, and translate their names | ||||
|             for direction in ('path-route-objects', 'reversed-path-route-objects'): | ||||
|                 i = 0 | ||||
|                 objects = response['path-properties'][direction] | ||||
|                 resulting_pro = [] | ||||
|                 last_name = None | ||||
|                 squashed_names = [] | ||||
|                 while i < len(objects): | ||||
|                     orig_name = objects[i]['path-route-object']['num-unnum-hop']['node-id'] | ||||
|                     translated_name = self.name_for(orig_name) | ||||
|                     if translated_name is None: | ||||
|                         # not an ONOS-level element | ||||
|                         i += 1 | ||||
|                         continue | ||||
|                     squashed_names.append(orig_name) | ||||
|                     if translated_name == last_name: | ||||
|                         resulting_pro.pop() | ||||
|                     last_name = translated_name | ||||
|                     resulting_pro.append(objects[i]) | ||||
|                     resulting_pro[-1]['path-route-object']['num-unnum-hop']['gnpy-nodes'] = copy.copy(squashed_names) | ||||
|                     resulting_pro[-1]['path-route-object']['num-unnum-hop']['node-id'] = translated_name | ||||
|                     resulting_pro[-1]['path-route-object']['num-unnum-hop']['link-tp-id'] = translated_name | ||||
|                     if len(squashed_names) > 1: | ||||
|                         resulting_pro[-1]['path-route-object']['num-unnum-hop']['gnpy-node-type'] = 'ROADM' | ||||
|                     i += 1 | ||||
|                     squashed_names.clear() | ||||
|                 response['path-properties'][direction] = resulting_pro | ||||
|         return responses | ||||
|  | ||||
|     def name_for(self, node_id): | ||||
|         return f'netconf:{self.mapping[node_id]}:830' if node_id in self.mapping else None | ||||
|  | ||||
|     def incoming_name_for(self, onos_name): | ||||
|         onos_name = onos_name[len('netconf:'):] | ||||
|         onos_name = onos_name[:-len(':830')] | ||||
|         return next(k for k, v in self.mapping.items() if v == onos_name) | ||||
|  | ||||
|  | ||||
| server = YangRunner() | ||||
| app = Flask('GNPy') | ||||
|  | ||||
|  | ||||
| @app.route('/gnpy-experimental/topology', methods=['POST']) | ||||
| def upload_yang(): | ||||
|     server.upload_equipment_and_network(request.json) | ||||
|     abort(Response(status=200)) | ||||
|  | ||||
|  | ||||
| @app.route('/gnpy-experimental', methods=['GET', 'POST']) | ||||
| def simulation(): | ||||
|     if server.network is None: | ||||
|         abort(Response(status=400, response='not provisioned yet')) | ||||
|     elif request.method == 'POST': | ||||
|         return {'result': {'response': server.handle_request_with_translation(request.json)}} | ||||
|     else: | ||||
|         return {'ping': True} | ||||
|  | ||||
|  | ||||
| @app.route('/gnpy-experimental/onos/devices') | ||||
| def show_onos_devices(): | ||||
|     if server.network is None: | ||||
|         abort(Response(status=400, response='not provisioned yet')) | ||||
|     return {'devices': server.onos_devices} | ||||
|  | ||||
|  | ||||
| @app.route('/gnpy-experimental/onos/links') | ||||
| def show_onos_links(): | ||||
|     if server.network is None: | ||||
|         abort(Response(status=400, response='not provisioned yet')) | ||||
|     return {'links': server.onos_links} | ||||
| @@ -21,7 +21,7 @@ from networkx import (dijkstra_path, NetworkXNoPath, | ||||
|                       all_simple_paths, shortest_simple_paths) | ||||
| from networkx.utils import pairwise | ||||
| from numpy import mean | ||||
| from gnpy.core.elements import Transceiver, Roadm | ||||
| from gnpy.core.elements import Transceiver, Roadm, Edfa | ||||
| from gnpy.core.utils import lin2db | ||||
| from gnpy.core.info import create_input_spectral_information | ||||
| from gnpy.core.exceptions import ServiceError, DisjunctionError | ||||
| @@ -145,53 +145,55 @@ class ResultElement: | ||||
|  | ||||
|     @property | ||||
|     def detailed_path_json(self): | ||||
|         return self.detailed_json_for_path(self.computed_path) | ||||
|  | ||||
|     @property | ||||
|     def detailed_reversed_path_json(self): | ||||
|         return self.detailed_json_for_path(self.reversed_computed_path) | ||||
|  | ||||
|     def detailed_json_for_path(self, path): | ||||
|         """ a function that builds path object for normal and blocking cases | ||||
|         """ | ||||
|         index = 0 | ||||
|         pro_list = [] | ||||
|         for element in self.computed_path: | ||||
|         for index, element in enumerate(path): | ||||
|             temp = { | ||||
|                 'path-route-object': { | ||||
|                     'index': index, | ||||
|                     'num-unnum-hop': { | ||||
|                         '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 self.path_request.M > 0: | ||||
|                 temp = { | ||||
|                     'path-route-object': { | ||||
|                         'index': index, | ||||
|                         "label-hop": { | ||||
|                             "N": self.path_request.N, | ||||
|                             "M": self.path_request.M | ||||
|                         }, | ||||
|                     } | ||||
|                 temp['path-route-object']["label-hop"] = { | ||||
|  | ||||
|                     "N": self.path_request.N, | ||||
|                     "M": self.path_request.M | ||||
|                 } | ||||
|                 pro_list.append(temp) | ||||
|                 index += 1 | ||||
|             elif self.path_request.M == 0 and hasattr(self.path_request, 'blocking_reason'): | ||||
|                 # if the path is blocked due to spectrum, no label object is created, but | ||||
|                 # the json response includes a detailed path for user infromation. | ||||
|                 pass | ||||
|             else: | ||||
|                 raise ServiceError('request {self.path_id} should have positive path bandwidth value.') | ||||
|  | ||||
|             if isinstance(element, Transceiver): | ||||
|                 temp = { | ||||
|                     'path-route-object': { | ||||
|                         'index': index, | ||||
|                         'transponder': { | ||||
|                             'transponder-type': self.path_request.tsp, | ||||
|                             'transponder-mode': self.path_request.tsp_mode | ||||
|                         } | ||||
|                     } | ||||
|                 temp['path-route-object']['num-unnum-hop']['gnpy-node-type'] = 'transceiver' | ||||
|                 temp['path-route-object']['num-unnum-hop']['transponder'] = { | ||||
|                     'transponder-type': self.path_request.tsp, | ||||
|                     'transponder-mode': self.path_request.tsp_mode | ||||
|                 } | ||||
|                 pro_list.append(temp) | ||||
|                 index += 1 | ||||
|             if isinstance(element, Edfa): | ||||
|                 temp['path-route-object']['num-unnum-hop']['gnpy-node-type'] = 'EDFA' | ||||
|                 temp['path-route-object']['num-unnum-hop']['target-channel-power'] = element.effective_pch_out_db | ||||
|                 temp['path-route-object']['output-voa']: element.out_voa | ||||
|             if isinstance(element, Roadm): | ||||
|                 temp['path-route-object']['num-unnum-hop']['gnpy-node-type'] = 'ROADM' | ||||
|                 temp['path-route-object']['num-unnum-hop']['target-channel-power'] = element.effective_pch_out_db | ||||
|  | ||||
|             pro_list.append(temp) | ||||
|         return pro_list | ||||
|  | ||||
|     @property | ||||
| @@ -231,7 +233,8 @@ class ResultElement: | ||||
|             path_properties = { | ||||
|                 'path-metric': path_metric(self.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, | ||||
|                 'reversed-path-route-objects': self.detailed_reversed_path_json, | ||||
|             } | ||||
|         else: | ||||
|             path_properties = { | ||||
|   | ||||
							
								
								
									
										25
									
								
								gnpy/yang/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								gnpy/yang/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| # SPDX-License-Identifier: BSD-3-Clause | ||||
| # | ||||
| # Copyright (C) 2020 Telecom Infra Project and GNPy contributors | ||||
| # see LICENSE.md for a list of contributors | ||||
|  | ||||
| ''' | ||||
| Working with YANG-encoded data | ||||
| ''' | ||||
|  | ||||
| from pathlib import Path | ||||
|  | ||||
|  | ||||
| def model_path() -> Path: | ||||
|     '''Filesystem path to TIP's own YANG models''' | ||||
|     return Path(__file__).parent / 'tip' | ||||
|  | ||||
|  | ||||
| def external_path() -> Path: | ||||
|     '''Filesystem path to third-party YANG models that are shipped with GNPy''' | ||||
|     return Path(__file__).parent / 'ext' | ||||
|  | ||||
|  | ||||
| def _yang_library() -> Path: | ||||
|     '''Filesystem path the the ietf-yanglib JSON file''' | ||||
|     return Path(__file__).parent / 'yanglib.json' | ||||
							
								
								
									
										24
									
								
								gnpy/yang/conversion.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								gnpy/yang/conversion.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| # SPDX-License-Identifier: BSD-3-Clause | ||||
| # | ||||
| # Copyright (C) 2020 Telecom Infra Project and GNPy contributors | ||||
| # see LICENSE.md for a list of contributors | ||||
|  | ||||
| """ | ||||
| Scaling factors for unit conversion | ||||
| =================================== | ||||
|  | ||||
| In YANG, the data model defines units for each possible value explicitly. | ||||
| This makes it possible for users to input data using the customary, common units. | ||||
| The :py:mod:`gnpy.yang.conversion` module holds scaling factors for conversion of SI units into YANG units and back. | ||||
| By convention, each items is used for multiplication when going from YANG to the legacy JSON. | ||||
| When converting from legacy JSON to YANG, use division. | ||||
| """ | ||||
|  | ||||
| import math | ||||
|  | ||||
| FIBER_DISPERSION = 1e-6 | ||||
| FIBER_DISPERSION_SLOPE = 1e3 | ||||
| FIBER_GAMMA = 1e-3 | ||||
| FIBER_PMD_COEF = 1e-14 * math.sqrt(10) | ||||
| THZ = 1e12 | ||||
| GIGA = 1000 * 1000 * 1000 | ||||
							
								
								
									
										458
									
								
								gnpy/yang/ext/ietf-inet-types@2013-07-15.yang
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										458
									
								
								gnpy/yang/ext/ietf-inet-types@2013-07-15.yang
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,458 @@ | ||||
| module ietf-inet-types { | ||||
|  | ||||
|   namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; | ||||
|   prefix "inet"; | ||||
|  | ||||
|   organization | ||||
|    "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; | ||||
|  | ||||
|   contact | ||||
|    "WG Web:   <http://tools.ietf.org/wg/netmod/> | ||||
|     WG List:  <mailto:netmod@ietf.org> | ||||
|  | ||||
|     WG Chair: David Kessens | ||||
|               <mailto:david.kessens@nsn.com> | ||||
|  | ||||
|     WG Chair: Juergen Schoenwaelder | ||||
|               <mailto:j.schoenwaelder@jacobs-university.de> | ||||
|  | ||||
|     Editor:   Juergen Schoenwaelder | ||||
|               <mailto:j.schoenwaelder@jacobs-university.de>"; | ||||
|  | ||||
|   description | ||||
|    "This module contains a collection of generally useful derived | ||||
|     YANG data types for Internet addresses and related things. | ||||
|  | ||||
|     Copyright (c) 2013 IETF Trust and the persons identified as | ||||
|     authors of the code.  All rights reserved. | ||||
|  | ||||
|     Redistribution and use in source and binary forms, with or | ||||
|     without modification, is permitted pursuant to, and subject | ||||
|     to the license terms contained in, the Simplified BSD License | ||||
|     set forth in Section 4.c of the IETF Trust's Legal Provisions | ||||
|     Relating to IETF Documents | ||||
|     (http://trustee.ietf.org/license-info). | ||||
|  | ||||
|     This version of this YANG module is part of RFC 6991; see | ||||
|     the RFC itself for full legal notices."; | ||||
|  | ||||
|   revision 2013-07-15 { | ||||
|     description | ||||
|      "This revision adds the following new data types: | ||||
|       - ip-address-no-zone | ||||
|       - ipv4-address-no-zone | ||||
|       - ipv6-address-no-zone"; | ||||
|     reference | ||||
|      "RFC 6991: Common YANG Data Types"; | ||||
|   } | ||||
|  | ||||
|   revision 2010-09-24 { | ||||
|     description | ||||
|      "Initial revision."; | ||||
|     reference | ||||
|      "RFC 6021: Common YANG Data Types"; | ||||
|   } | ||||
|  | ||||
|   /*** collection of types related to protocol fields ***/ | ||||
|  | ||||
|   typedef ip-version { | ||||
|     type enumeration { | ||||
|       enum unknown { | ||||
|         value "0"; | ||||
|         description | ||||
|          "An unknown or unspecified version of the Internet | ||||
|           protocol."; | ||||
|       } | ||||
|       enum ipv4 { | ||||
|         value "1"; | ||||
|         description | ||||
|          "The IPv4 protocol as defined in RFC 791."; | ||||
|       } | ||||
|       enum ipv6 { | ||||
|         value "2"; | ||||
|         description | ||||
|          "The IPv6 protocol as defined in RFC 2460."; | ||||
|       } | ||||
|     } | ||||
|     description | ||||
|      "This value represents the version of the IP protocol. | ||||
|  | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the InetVersion textual convention of the SMIv2."; | ||||
|     reference | ||||
|      "RFC  791: Internet Protocol | ||||
|       RFC 2460: Internet Protocol, Version 6 (IPv6) Specification | ||||
|       RFC 4001: Textual Conventions for Internet Network Addresses"; | ||||
|   } | ||||
|  | ||||
|   typedef dscp { | ||||
|     type uint8 { | ||||
|       range "0..63"; | ||||
|     } | ||||
|     description | ||||
|      "The dscp type represents a Differentiated Services Code Point | ||||
|       that may be used for marking packets in a traffic stream. | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the Dscp textual convention of the SMIv2."; | ||||
|     reference | ||||
|      "RFC 3289: Management Information Base for the Differentiated | ||||
|                 Services Architecture | ||||
|       RFC 2474: Definition of the Differentiated Services Field | ||||
|                 (DS Field) in the IPv4 and IPv6 Headers | ||||
|       RFC 2780: IANA Allocation Guidelines For Values In | ||||
|                 the Internet Protocol and Related Headers"; | ||||
|   } | ||||
|  | ||||
|   typedef ipv6-flow-label { | ||||
|     type uint32 { | ||||
|       range "0..1048575"; | ||||
|     } | ||||
|     description | ||||
|      "The ipv6-flow-label type represents the flow identifier or Flow | ||||
|       Label in an IPv6 packet header that may be used to | ||||
|       discriminate traffic flows. | ||||
|  | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the IPv6FlowLabel textual convention of the SMIv2."; | ||||
|     reference | ||||
|      "RFC 3595: Textual Conventions for IPv6 Flow Label | ||||
|       RFC 2460: Internet Protocol, Version 6 (IPv6) Specification"; | ||||
|   } | ||||
|  | ||||
|   typedef port-number { | ||||
|     type uint16 { | ||||
|       range "0..65535"; | ||||
|     } | ||||
|     description | ||||
|      "The port-number type represents a 16-bit port number of an | ||||
|       Internet transport-layer protocol such as UDP, TCP, DCCP, or | ||||
|       SCTP.  Port numbers are assigned by IANA.  A current list of | ||||
|       all assignments is available from <http://www.iana.org/>. | ||||
|  | ||||
|       Note that the port number value zero is reserved by IANA.  In | ||||
|       situations where the value zero does not make sense, it can | ||||
|       be excluded by subtyping the port-number type. | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the InetPortNumber textual convention of the SMIv2."; | ||||
|     reference | ||||
|      "RFC  768: User Datagram Protocol | ||||
|       RFC  793: Transmission Control Protocol | ||||
|       RFC 4960: Stream Control Transmission Protocol | ||||
|       RFC 4340: Datagram Congestion Control Protocol (DCCP) | ||||
|       RFC 4001: Textual Conventions for Internet Network Addresses"; | ||||
|   } | ||||
|  | ||||
|   /*** collection of types related to autonomous systems ***/ | ||||
|  | ||||
|   typedef as-number { | ||||
|     type uint32; | ||||
|     description | ||||
|      "The as-number type represents autonomous system numbers | ||||
|       which identify an Autonomous System (AS).  An AS is a set | ||||
|       of routers under a single technical administration, using | ||||
|       an interior gateway protocol and common metrics to route | ||||
|       packets within the AS, and using an exterior gateway | ||||
|       protocol to route packets to other ASes.  IANA maintains | ||||
|       the AS number space and has delegated large parts to the | ||||
|       regional registries. | ||||
|  | ||||
|       Autonomous system numbers were originally limited to 16 | ||||
|       bits.  BGP extensions have enlarged the autonomous system | ||||
|       number space to 32 bits.  This type therefore uses an uint32 | ||||
|       base type without a range restriction in order to support | ||||
|       a larger autonomous system number space. | ||||
|  | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the InetAutonomousSystemNumber textual convention of | ||||
|       the SMIv2."; | ||||
|     reference | ||||
|      "RFC 1930: Guidelines for creation, selection, and registration | ||||
|                 of an Autonomous System (AS) | ||||
|       RFC 4271: A Border Gateway Protocol 4 (BGP-4) | ||||
|       RFC 4001: Textual Conventions for Internet Network Addresses | ||||
|       RFC 6793: BGP Support for Four-Octet Autonomous System (AS) | ||||
|                 Number Space"; | ||||
|   } | ||||
|  | ||||
|   /*** collection of types related to IP addresses and hostnames ***/ | ||||
|  | ||||
|   typedef ip-address { | ||||
|     type union { | ||||
|       type inet:ipv4-address; | ||||
|       type inet:ipv6-address; | ||||
|     } | ||||
|     description | ||||
|      "The ip-address type represents an IP address and is IP | ||||
|       version neutral.  The format of the textual representation | ||||
|       implies the IP version.  This type supports scoped addresses | ||||
|       by allowing zone identifiers in the address format."; | ||||
|     reference | ||||
|      "RFC 4007: IPv6 Scoped Address Architecture"; | ||||
|   } | ||||
|  | ||||
|   typedef ipv4-address { | ||||
|     type string { | ||||
|       pattern | ||||
|         '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' | ||||
|       +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' | ||||
|       + '(%[\p{N}\p{L}]+)?'; | ||||
|     } | ||||
|     description | ||||
|       "The ipv4-address type represents an IPv4 address in | ||||
|        dotted-quad notation.  The IPv4 address may include a zone | ||||
|        index, separated by a % sign. | ||||
|  | ||||
|        The zone index is used to disambiguate identical address | ||||
|        values.  For link-local addresses, the zone index will | ||||
|        typically be the interface index number or the name of an | ||||
|        interface.  If the zone index is not present, the default | ||||
|        zone of the device will be used. | ||||
|  | ||||
|        The canonical format for the zone index is the numerical | ||||
|        format"; | ||||
|   } | ||||
|  | ||||
|   typedef ipv6-address { | ||||
|     type string { | ||||
|       pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' | ||||
|             + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' | ||||
|             + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' | ||||
|             + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' | ||||
|             + '(%[\p{N}\p{L}]+)?'; | ||||
|       pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' | ||||
|             + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' | ||||
|             + '(%.+)?'; | ||||
|     } | ||||
|     description | ||||
|      "The ipv6-address type represents an IPv6 address in full, | ||||
|       mixed, shortened, and shortened-mixed notation.  The IPv6 | ||||
|       address may include a zone index, separated by a % sign. | ||||
|  | ||||
|       The zone index is used to disambiguate identical address | ||||
|       values.  For link-local addresses, the zone index will | ||||
|       typically be the interface index number or the name of an | ||||
|       interface.  If the zone index is not present, the default | ||||
|       zone of the device will be used. | ||||
|  | ||||
|       The canonical format of IPv6 addresses uses the textual | ||||
|       representation defined in Section 4 of RFC 5952.  The | ||||
|       canonical format for the zone index is the numerical | ||||
|       format as described in Section 11.2 of RFC 4007."; | ||||
|     reference | ||||
|      "RFC 4291: IP Version 6 Addressing Architecture | ||||
|       RFC 4007: IPv6 Scoped Address Architecture | ||||
|       RFC 5952: A Recommendation for IPv6 Address Text | ||||
|                 Representation"; | ||||
|   } | ||||
|  | ||||
|   typedef ip-address-no-zone { | ||||
|     type union { | ||||
|       type inet:ipv4-address-no-zone; | ||||
|       type inet:ipv6-address-no-zone; | ||||
|     } | ||||
|     description | ||||
|      "The ip-address-no-zone type represents an IP address and is | ||||
|       IP version neutral.  The format of the textual representation | ||||
|       implies the IP version.  This type does not support scoped | ||||
|       addresses since it does not allow zone identifiers in the | ||||
|       address format."; | ||||
|     reference | ||||
|      "RFC 4007: IPv6 Scoped Address Architecture"; | ||||
|   } | ||||
|  | ||||
|   typedef ipv4-address-no-zone { | ||||
|     type inet:ipv4-address { | ||||
|       pattern '[0-9\.]*'; | ||||
|     } | ||||
|     description | ||||
|       "An IPv4 address without a zone index.  This type, derived from | ||||
|        ipv4-address, may be used in situations where the zone is | ||||
|        known from the context and hence no zone index is needed."; | ||||
|   } | ||||
|  | ||||
|   typedef ipv6-address-no-zone { | ||||
|     type inet:ipv6-address { | ||||
|       pattern '[0-9a-fA-F:\.]*'; | ||||
|     } | ||||
|     description | ||||
|       "An IPv6 address without a zone index.  This type, derived from | ||||
|        ipv6-address, may be used in situations where the zone is | ||||
|        known from the context and hence no zone index is needed."; | ||||
|     reference | ||||
|      "RFC 4291: IP Version 6 Addressing Architecture | ||||
|       RFC 4007: IPv6 Scoped Address Architecture | ||||
|       RFC 5952: A Recommendation for IPv6 Address Text | ||||
|                 Representation"; | ||||
|   } | ||||
|  | ||||
|   typedef ip-prefix { | ||||
|     type union { | ||||
|       type inet:ipv4-prefix; | ||||
|       type inet:ipv6-prefix; | ||||
|     } | ||||
|     description | ||||
|      "The ip-prefix type represents an IP prefix and is IP | ||||
|       version neutral.  The format of the textual representations | ||||
|       implies the IP version."; | ||||
|   } | ||||
|  | ||||
|   typedef ipv4-prefix { | ||||
|     type string { | ||||
|       pattern | ||||
|          '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' | ||||
|        +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' | ||||
|        + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; | ||||
|     } | ||||
|     description | ||||
|      "The ipv4-prefix type represents an IPv4 address prefix. | ||||
|       The prefix length is given by the number following the | ||||
|       slash character and must be less than or equal to 32. | ||||
|  | ||||
|       A prefix length value of n corresponds to an IP address | ||||
|       mask that has n contiguous 1-bits from the most | ||||
|       significant bit (MSB) and all other bits set to 0. | ||||
|  | ||||
|       The canonical format of an IPv4 prefix has all bits of | ||||
|       the IPv4 address set to zero that are not part of the | ||||
|       IPv4 prefix."; | ||||
|   } | ||||
|  | ||||
|   typedef ipv6-prefix { | ||||
|     type string { | ||||
|       pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' | ||||
|             + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' | ||||
|             + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' | ||||
|             + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' | ||||
|             + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; | ||||
|       pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' | ||||
|             + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' | ||||
|             + '(/.+)'; | ||||
|     } | ||||
|  | ||||
|     description | ||||
|      "The ipv6-prefix type represents an IPv6 address prefix. | ||||
|       The prefix length is given by the number following the | ||||
|       slash character and must be less than or equal to 128. | ||||
|  | ||||
|       A prefix length value of n corresponds to an IP address | ||||
|       mask that has n contiguous 1-bits from the most | ||||
|       significant bit (MSB) and all other bits set to 0. | ||||
|  | ||||
|       The IPv6 address should have all bits that do not belong | ||||
|       to the prefix set to zero. | ||||
|  | ||||
|       The canonical format of an IPv6 prefix has all bits of | ||||
|       the IPv6 address set to zero that are not part of the | ||||
|       IPv6 prefix.  Furthermore, the IPv6 address is represented | ||||
|       as defined in Section 4 of RFC 5952."; | ||||
|     reference | ||||
|      "RFC 5952: A Recommendation for IPv6 Address Text | ||||
|                 Representation"; | ||||
|   } | ||||
|  | ||||
|   /*** collection of domain name and URI types ***/ | ||||
|  | ||||
|   typedef domain-name { | ||||
|     type string { | ||||
|       pattern | ||||
|         '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*' | ||||
|       + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)' | ||||
|       + '|\.'; | ||||
|       length "1..253"; | ||||
|     } | ||||
|     description | ||||
|      "The domain-name type represents a DNS domain name.  The | ||||
|       name SHOULD be fully qualified whenever possible. | ||||
|  | ||||
|       Internet domain names are only loosely specified.  Section | ||||
|       3.5 of RFC 1034 recommends a syntax (modified in Section | ||||
|       2.1 of RFC 1123).  The pattern above is intended to allow | ||||
|       for current practice in domain name use, and some possible | ||||
|       future expansion.  It is designed to hold various types of | ||||
|       domain names, including names used for A or AAAA records | ||||
|       (host names) and other records, such as SRV records.  Note | ||||
|       that Internet host names have a stricter syntax (described | ||||
|       in RFC 952) than the DNS recommendations in RFCs 1034 and | ||||
|       1123, and that systems that want to store host names in | ||||
|       schema nodes using the domain-name type are recommended to | ||||
|       adhere to this stricter standard to ensure interoperability. | ||||
|  | ||||
|       The encoding of DNS names in the DNS protocol is limited | ||||
|       to 255 characters.  Since the encoding consists of labels | ||||
|       prefixed by a length bytes and there is a trailing NULL | ||||
|       byte, only 253 characters can appear in the textual dotted | ||||
|       notation. | ||||
|  | ||||
|       The description clause of schema nodes using the domain-name | ||||
|       type MUST describe when and how these names are resolved to | ||||
|       IP addresses.  Note that the resolution of a domain-name value | ||||
|       may require to query multiple DNS records (e.g., A for IPv4 | ||||
|       and AAAA for IPv6).  The order of the resolution process and | ||||
|       which DNS record takes precedence can either be defined | ||||
|       explicitly or may depend on the configuration of the | ||||
|       resolver. | ||||
|  | ||||
|       Domain-name values use the US-ASCII encoding.  Their canonical | ||||
|       format uses lowercase US-ASCII characters.  Internationalized | ||||
|       domain names MUST be A-labels as per RFC 5890."; | ||||
|     reference | ||||
|      "RFC  952: DoD Internet Host Table Specification | ||||
|       RFC 1034: Domain Names - Concepts and Facilities | ||||
|       RFC 1123: Requirements for Internet Hosts -- Application | ||||
|                 and Support | ||||
|       RFC 2782: A DNS RR for specifying the location of services | ||||
|                 (DNS SRV) | ||||
|       RFC 5890: Internationalized Domain Names in Applications | ||||
|                 (IDNA): Definitions and Document Framework"; | ||||
|   } | ||||
|  | ||||
|   typedef host { | ||||
|     type union { | ||||
|       type inet:ip-address; | ||||
|       type inet:domain-name; | ||||
|     } | ||||
|     description | ||||
|      "The host type represents either an IP address or a DNS | ||||
|       domain name."; | ||||
|   } | ||||
|  | ||||
|   typedef uri { | ||||
|     type string; | ||||
|     description | ||||
|      "The uri type represents a Uniform Resource Identifier | ||||
|       (URI) as defined by STD 66. | ||||
|  | ||||
|       Objects using the uri type MUST be in US-ASCII encoding, | ||||
|       and MUST be normalized as described by RFC 3986 Sections | ||||
|       6.2.1, 6.2.2.1, and 6.2.2.2.  All unnecessary | ||||
|       percent-encoding is removed, and all case-insensitive | ||||
|       characters are set to lowercase except for hexadecimal | ||||
|       digits, which are normalized to uppercase as described in | ||||
|       Section 6.2.2.1. | ||||
|  | ||||
|       The purpose of this normalization is to help provide | ||||
|       unique URIs.  Note that this normalization is not | ||||
|       sufficient to provide uniqueness.  Two URIs that are | ||||
|       textually distinct after this normalization may still be | ||||
|       equivalent. | ||||
|  | ||||
|       Objects using the uri type may restrict the schemes that | ||||
|       they permit.  For example, 'data:' and 'urn:' schemes | ||||
|       might not be appropriate. | ||||
|  | ||||
|       A zero-length URI is not a valid URI.  This can be used to | ||||
|       express 'URI absent' where required. | ||||
|  | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the Uri SMIv2 textual convention defined in RFC 5017."; | ||||
|     reference | ||||
|      "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax | ||||
|       RFC 3305: Report from the Joint W3C/IETF URI Planning Interest | ||||
|                 Group: Uniform Resource Identifiers (URIs), URLs, | ||||
|                 and Uniform Resource Names (URNs): Clarifications | ||||
|                 and Recommendations | ||||
|       RFC 5017: MIB Textual Conventions for Uniform Resource | ||||
|                 Identifiers (URIs)"; | ||||
|   } | ||||
|  | ||||
| } | ||||
							
								
								
									
										294
									
								
								gnpy/yang/ext/ietf-network-topology@2018-02-26.yang
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										294
									
								
								gnpy/yang/ext/ietf-network-topology@2018-02-26.yang
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,294 @@ | ||||
| module ietf-network-topology { | ||||
|   yang-version 1.1; | ||||
|   namespace "urn:ietf:params:xml:ns:yang:ietf-network-topology"; | ||||
|   prefix nt; | ||||
|  | ||||
|   import ietf-inet-types { | ||||
|     prefix inet; | ||||
|     reference | ||||
|       "RFC 6991: Common YANG Data Types"; | ||||
|   } | ||||
|   import ietf-network { | ||||
|     prefix nw; | ||||
|     reference | ||||
|       "RFC 8345: A YANG Data Model for Network Topologies"; | ||||
|   } | ||||
|  | ||||
|   organization | ||||
|     "IETF I2RS (Interface to the Routing System) Working Group"; | ||||
|  | ||||
|   contact | ||||
|     "WG Web:    <https://datatracker.ietf.org/wg/i2rs/> | ||||
|      WG List:   <mailto:i2rs@ietf.org> | ||||
|  | ||||
|      Editor:    Alexander Clemm | ||||
|                 <mailto:ludwig@clemm.org> | ||||
|  | ||||
|      Editor:    Jan Medved | ||||
|                 <mailto:jmedved@cisco.com> | ||||
|  | ||||
|      Editor:    Robert Varga | ||||
|                 <mailto:robert.varga@pantheon.tech> | ||||
|  | ||||
|      Editor:    Nitin Bahadur | ||||
|                 <mailto:nitin_bahadur@yahoo.com> | ||||
|  | ||||
|      Editor:    Hariharan Ananthakrishnan | ||||
|                 <mailto:hari@packetdesign.com> | ||||
|  | ||||
|      Editor:    Xufeng Liu | ||||
|                 <mailto:xufeng.liu.ietf@gmail.com>"; | ||||
|  | ||||
|   description | ||||
|     "This module defines a common base model for a network topology, | ||||
|      augmenting the base network data model with links to connect | ||||
|      nodes, as well as termination points to terminate links | ||||
|      on nodes. | ||||
|  | ||||
|      Copyright (c) 2018 IETF Trust and the persons identified as | ||||
|      authors of the code.  All rights reserved. | ||||
|  | ||||
|      Redistribution and use in source and binary forms, with or | ||||
|      without modification, is permitted pursuant to, and subject | ||||
|      to the license terms contained in, the Simplified BSD License | ||||
|      set forth in Section 4.c of the IETF Trust's Legal Provisions | ||||
|      Relating to IETF Documents | ||||
|      (https://trustee.ietf.org/license-info). | ||||
|  | ||||
|      This version of this YANG module is part of RFC 8345; | ||||
|      see the RFC itself for full legal notices."; | ||||
|  | ||||
|   revision 2018-02-26 { | ||||
|     description | ||||
|       "Initial revision."; | ||||
|     reference | ||||
|       "RFC 8345: A YANG Data Model for Network Topologies"; | ||||
|   } | ||||
|  | ||||
|   typedef link-id { | ||||
|     type inet:uri; | ||||
|     description | ||||
|       "An identifier for a link in a topology.  The precise | ||||
|        structure of the link-id will be up to the implementation. | ||||
|        The identifier SHOULD be chosen such that the same link in a | ||||
|        real network topology will always be identified through the | ||||
|        same identifier, even if the data model is instantiated in | ||||
|        separate datastores.  An implementation MAY choose to capture | ||||
|        semantics in the identifier -- for example, to indicate the | ||||
|        type of link and/or the type of topology of which the link is | ||||
|        a part."; | ||||
|   } | ||||
|  | ||||
|   typedef tp-id { | ||||
|     type inet:uri; | ||||
|     description | ||||
|       "An identifier for termination points on a node.  The precise | ||||
|        structure of the tp-id will be up to the implementation. | ||||
|        The identifier SHOULD be chosen such that the same termination | ||||
|        point in a real network topology will always be identified | ||||
|        through the same identifier, even if the data model is | ||||
|        instantiated in separate datastores.  An implementation MAY | ||||
|        choose to capture semantics in the identifier -- for example, | ||||
|        to indicate the type of termination point and/or the type of | ||||
|        node that contains the termination point."; | ||||
|   } | ||||
|  | ||||
|   grouping link-ref { | ||||
|     description | ||||
|       "This grouping can be used to reference a link in a specific | ||||
|        network.  Although it is not used in this module, it is | ||||
|        defined here for the convenience of augmenting modules."; | ||||
|     leaf link-ref { | ||||
|       type leafref { | ||||
|         path "/nw:networks/nw:network[nw:network-id=current()/../"+ | ||||
|           "network-ref]/nt:link/nt:link-id"; | ||||
|         require-instance false; | ||||
|       } | ||||
|       description | ||||
|         "A type for an absolute reference to a link instance. | ||||
|          (This type should not be used for relative references. | ||||
|          In such a case, a relative path should be used instead.)"; | ||||
|     } | ||||
|     uses nw:network-ref; | ||||
|   } | ||||
|  | ||||
|   grouping tp-ref { | ||||
|     description | ||||
|       "This grouping can be used to reference a termination point | ||||
|        in a specific node.  Although it is not used in this module, | ||||
|        it is defined here for the convenience of augmenting | ||||
|        modules."; | ||||
|     leaf tp-ref { | ||||
|       type leafref { | ||||
|         path "/nw:networks/nw:network[nw:network-id=current()/../"+ | ||||
|           "network-ref]/nw:node[nw:node-id=current()/../"+ | ||||
|           "node-ref]/nt:termination-point/nt:tp-id"; | ||||
|         require-instance false; | ||||
|       } | ||||
|       description | ||||
|         "A type for an absolute reference to a termination point. | ||||
|          (This type should not be used for relative references. | ||||
|          In such a case, a relative path should be used instead.)"; | ||||
|     } | ||||
|     uses nw:node-ref; | ||||
|   } | ||||
|  | ||||
|   augment "/nw:networks/nw:network" { | ||||
|     description | ||||
|       "Add links to the network data model."; | ||||
|     list link { | ||||
|       key "link-id"; | ||||
|       description | ||||
|         "A network link connects a local (source) node and | ||||
|          a remote (destination) node via a set of the respective | ||||
|          node's termination points.  It is possible to have several | ||||
|          links between the same source and destination nodes. | ||||
|          Likewise, a link could potentially be re-homed between | ||||
|          termination points.  Therefore, in order to ensure that we | ||||
|          would always know to distinguish between links, every link | ||||
|          is identified by a dedicated link identifier.  Note that a | ||||
|          link models a point-to-point link, not a multipoint link."; | ||||
|       leaf link-id { | ||||
|         type link-id; | ||||
|         description | ||||
|           "The identifier of a link in the topology. | ||||
|            A link is specific to a topology to which it belongs."; | ||||
|       } | ||||
|       container source { | ||||
|         description | ||||
|           "This container holds the logical source of a particular | ||||
|            link."; | ||||
|         leaf source-node { | ||||
|           type leafref { | ||||
|             path "../../../nw:node/nw:node-id"; | ||||
|             require-instance false; | ||||
|           } | ||||
|           description | ||||
|             "Source node identifier.  Must be in the same topology."; | ||||
|         } | ||||
|         leaf source-tp { | ||||
|           type leafref { | ||||
|             path "../../../nw:node[nw:node-id=current()/../"+ | ||||
|               "source-node]/termination-point/tp-id"; | ||||
|             require-instance false; | ||||
|           } | ||||
|           description | ||||
|             "This termination point is located within the source node | ||||
|              and terminates the link."; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       container destination { | ||||
|         description | ||||
|           "This container holds the logical destination of a | ||||
|            particular link."; | ||||
|         leaf dest-node { | ||||
|           type leafref { | ||||
|             path "../../../nw:node/nw:node-id"; | ||||
|           require-instance false; | ||||
|           } | ||||
|           description | ||||
|             "Destination node identifier.  Must be in the same | ||||
|              network."; | ||||
|         } | ||||
|         leaf dest-tp { | ||||
|           type leafref { | ||||
|             path "../../../nw:node[nw:node-id=current()/../"+ | ||||
|               "dest-node]/termination-point/tp-id"; | ||||
|             require-instance false; | ||||
|           } | ||||
|           description | ||||
|             "This termination point is located within the | ||||
|              destination node and terminates the link."; | ||||
|         } | ||||
|       } | ||||
|       list supporting-link { | ||||
|         key "network-ref link-ref"; | ||||
|         description | ||||
|           "Identifies the link or links on which this link depends."; | ||||
|         leaf network-ref { | ||||
|           type leafref { | ||||
|             path "../../../nw:supporting-network/nw:network-ref"; | ||||
|           require-instance false; | ||||
|           } | ||||
|           description | ||||
|             "This leaf identifies in which underlay topology | ||||
|              the supporting link is present."; | ||||
|         } | ||||
|  | ||||
|         leaf link-ref { | ||||
|           type leafref { | ||||
|             path "/nw:networks/nw:network[nw:network-id=current()/"+ | ||||
|               "../network-ref]/link/link-id"; | ||||
|             require-instance false; | ||||
|           } | ||||
|           description | ||||
|             "This leaf identifies a link that is a part | ||||
|              of this link's underlay.  Reference loops in which | ||||
|              a link identifies itself as its underlay, either | ||||
|              directly or transitively, are not allowed."; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   augment "/nw:networks/nw:network/nw:node" { | ||||
|     description | ||||
|       "Augments termination points that terminate links. | ||||
|        Termination points can ultimately be mapped to interfaces."; | ||||
|     list termination-point { | ||||
|       key "tp-id"; | ||||
|       description | ||||
|         "A termination point can terminate a link. | ||||
|          Depending on the type of topology, a termination point | ||||
|          could, for example, refer to a port or an interface."; | ||||
|       leaf tp-id { | ||||
|         type tp-id; | ||||
|         description | ||||
|           "Termination point identifier."; | ||||
|       } | ||||
|       list supporting-termination-point { | ||||
|         key "network-ref node-ref tp-ref"; | ||||
|         description | ||||
|           "This list identifies any termination points on which a | ||||
|            given termination point depends or onto which it maps. | ||||
|            Those termination points will themselves be contained | ||||
|            in a supporting node.  This dependency information can be | ||||
|            inferred from the dependencies between links.  Therefore, | ||||
|            this item is not separately configurable.  Hence, no | ||||
|            corresponding constraint needs to be articulated. | ||||
|            The corresponding information is simply provided by the | ||||
|            implementing system."; | ||||
|  | ||||
|         leaf network-ref { | ||||
|           type leafref { | ||||
|             path "../../../nw:supporting-node/nw:network-ref"; | ||||
|           require-instance false; | ||||
|           } | ||||
|           description | ||||
|             "This leaf identifies in which topology the | ||||
|              supporting termination point is present."; | ||||
|         } | ||||
|         leaf node-ref { | ||||
|           type leafref { | ||||
|             path "../../../nw:supporting-node/nw:node-ref"; | ||||
|           require-instance false; | ||||
|           } | ||||
|           description | ||||
|             "This leaf identifies in which node the supporting | ||||
|              termination point is present."; | ||||
|         } | ||||
|         leaf tp-ref { | ||||
|           type leafref { | ||||
|             path "/nw:networks/nw:network[nw:network-id=current()/"+ | ||||
|               "../network-ref]/nw:node[nw:node-id=current()/../"+ | ||||
|               "node-ref]/termination-point/tp-id"; | ||||
|             require-instance false; | ||||
|           } | ||||
|           description | ||||
|             "Reference to the underlay node (the underlay node must | ||||
|              be in a different topology)."; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										192
									
								
								gnpy/yang/ext/ietf-network@2018-02-26.yang
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								gnpy/yang/ext/ietf-network@2018-02-26.yang
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,192 @@ | ||||
| module ietf-network { | ||||
|   yang-version 1.1; | ||||
|   namespace "urn:ietf:params:xml:ns:yang:ietf-network"; | ||||
|   prefix nw; | ||||
|  | ||||
|   import ietf-inet-types { | ||||
|     prefix inet; | ||||
|     reference | ||||
|       "RFC 6991: Common YANG Data Types"; | ||||
|   } | ||||
|  | ||||
|   organization | ||||
|     "IETF I2RS (Interface to the Routing System) Working Group"; | ||||
|  | ||||
|   contact | ||||
|     "WG Web:    <https://datatracker.ietf.org/wg/i2rs/> | ||||
|      WG List:   <mailto:i2rs@ietf.org> | ||||
|  | ||||
|      Editor:    Alexander Clemm | ||||
|                 <mailto:ludwig@clemm.org> | ||||
|  | ||||
|      Editor:    Jan Medved | ||||
|                 <mailto:jmedved@cisco.com> | ||||
|  | ||||
|      Editor:    Robert Varga | ||||
|                 <mailto:robert.varga@pantheon.tech> | ||||
|  | ||||
|      Editor:    Nitin Bahadur | ||||
|                 <mailto:nitin_bahadur@yahoo.com> | ||||
|  | ||||
|      Editor:    Hariharan Ananthakrishnan | ||||
|                 <mailto:hari@packetdesign.com> | ||||
|  | ||||
|      Editor:    Xufeng Liu | ||||
|                 <mailto:xufeng.liu.ietf@gmail.com>"; | ||||
|   description | ||||
|     "This module defines a common base data model for a collection | ||||
|      of nodes in a network.  Node definitions are further used | ||||
|      in network topologies and inventories. | ||||
|  | ||||
|      Copyright (c) 2018 IETF Trust and the persons identified as | ||||
|      authors of the code.  All rights reserved. | ||||
|  | ||||
|      Redistribution and use in source and binary forms, with or | ||||
|      without modification, is permitted pursuant to, and subject | ||||
|      to the license terms contained in, the Simplified BSD License | ||||
|      set forth in Section 4.c of the IETF Trust's Legal Provisions | ||||
|      Relating to IETF Documents | ||||
|      (https://trustee.ietf.org/license-info). | ||||
|  | ||||
|      This version of this YANG module is part of RFC 8345; | ||||
|      see the RFC itself for full legal notices."; | ||||
|  | ||||
|   revision 2018-02-26 { | ||||
|     description | ||||
|       "Initial revision."; | ||||
|     reference | ||||
|       "RFC 8345: A YANG Data Model for Network Topologies"; | ||||
|   } | ||||
|  | ||||
|   typedef node-id { | ||||
|     type inet:uri; | ||||
|     description | ||||
|       "Identifier for a node.  The precise structure of the node-id | ||||
|        will be up to the implementation.  For example, some | ||||
|        implementations MAY pick a URI that includes the network-id | ||||
|        as part of the path.  The identifier SHOULD be chosen | ||||
|        such that the same node in a real network topology will | ||||
|        always be identified through the same identifier, even if | ||||
|        the data model is instantiated in separate datastores.  An | ||||
|        implementation MAY choose to capture semantics in the | ||||
|        identifier -- for example, to indicate the type of node."; | ||||
|   } | ||||
|  | ||||
|   typedef network-id { | ||||
|     type inet:uri; | ||||
|     description | ||||
|       "Identifier for a network.  The precise structure of the | ||||
|        network-id will be up to the implementation.  The identifier | ||||
|        SHOULD be chosen such that the same network will always be | ||||
|        identified through the same identifier, even if the data model | ||||
|        is instantiated in separate datastores.  An implementation MAY | ||||
|        choose to capture semantics in the identifier -- for example, | ||||
|        to indicate the type of network."; | ||||
|   } | ||||
|  | ||||
|   grouping network-ref { | ||||
|     description | ||||
|       "Contains the information necessary to reference a network -- | ||||
|        for example, an underlay network."; | ||||
|     leaf network-ref { | ||||
|       type leafref { | ||||
|         path "/nw:networks/nw:network/nw:network-id"; | ||||
|       require-instance false; | ||||
|       } | ||||
|       description | ||||
|         "Used to reference a network -- for example, an underlay | ||||
|          network."; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   grouping node-ref { | ||||
|     description | ||||
|       "Contains the information necessary to reference a node."; | ||||
|     leaf node-ref { | ||||
|       type leafref { | ||||
|         path "/nw:networks/nw:network[nw:network-id=current()/../"+ | ||||
|           "network-ref]/nw:node/nw:node-id"; | ||||
|         require-instance false; | ||||
|       } | ||||
|       description | ||||
|         "Used to reference a node. | ||||
|          Nodes are identified relative to the network that | ||||
|          contains them."; | ||||
|     } | ||||
|     uses network-ref; | ||||
|   } | ||||
|  | ||||
|   container networks { | ||||
|     description | ||||
|       "Serves as a top-level container for a list of networks."; | ||||
|     list network { | ||||
|       key "network-id"; | ||||
|       description | ||||
|         "Describes a network. | ||||
|          A network typically contains an inventory of nodes, | ||||
|          topological information (augmented through the | ||||
|          network-topology data model), and layering information."; | ||||
|       leaf network-id { | ||||
|         type network-id; | ||||
|         description | ||||
|           "Identifies a network."; | ||||
|       } | ||||
|       container network-types { | ||||
|         description | ||||
|           "Serves as an augmentation target. | ||||
|            The network type is indicated through corresponding | ||||
|            presence containers augmented into this container."; | ||||
|       } | ||||
|       list supporting-network { | ||||
|         key "network-ref"; | ||||
|         description | ||||
|           "An underlay network, used to represent layered network | ||||
|            topologies."; | ||||
|         leaf network-ref { | ||||
|           type leafref { | ||||
|             path "/nw:networks/nw:network/nw:network-id"; | ||||
|           require-instance false; | ||||
|           } | ||||
|           description | ||||
|             "References the underlay network."; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       list node { | ||||
|         key "node-id"; | ||||
|         description | ||||
|           "The inventory of nodes of this network."; | ||||
|         leaf node-id { | ||||
|           type node-id; | ||||
|           description | ||||
|             "Uniquely identifies a node within the containing | ||||
|              network."; | ||||
|         } | ||||
|         list supporting-node { | ||||
|           key "network-ref node-ref"; | ||||
|           description | ||||
|             "Represents another node that is in an underlay network | ||||
|              and that supports this node.  Used to represent layering | ||||
|              structure."; | ||||
|           leaf network-ref { | ||||
|             type leafref { | ||||
|               path "../../../nw:supporting-network/nw:network-ref"; | ||||
|             require-instance false; | ||||
|             } | ||||
|             description | ||||
|               "References the underlay network of which the | ||||
|                underlay node is a part."; | ||||
|           } | ||||
|           leaf node-ref { | ||||
|             type leafref { | ||||
|               path "/nw:networks/nw:network/nw:node/nw:node-id"; | ||||
|             require-instance false; | ||||
|             } | ||||
|             description | ||||
|               "References the underlay node itself."; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										474
									
								
								gnpy/yang/ext/ietf-yang-types@2013-07-15.yang
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										474
									
								
								gnpy/yang/ext/ietf-yang-types@2013-07-15.yang
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,474 @@ | ||||
| module ietf-yang-types { | ||||
|  | ||||
|   namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; | ||||
|   prefix "yang"; | ||||
|  | ||||
|   organization | ||||
|    "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; | ||||
|  | ||||
|   contact | ||||
|    "WG Web:   <http://tools.ietf.org/wg/netmod/> | ||||
|     WG List:  <mailto:netmod@ietf.org> | ||||
|  | ||||
|     WG Chair: David Kessens | ||||
|               <mailto:david.kessens@nsn.com> | ||||
|  | ||||
|     WG Chair: Juergen Schoenwaelder | ||||
|               <mailto:j.schoenwaelder@jacobs-university.de> | ||||
|  | ||||
|     Editor:   Juergen Schoenwaelder | ||||
|               <mailto:j.schoenwaelder@jacobs-university.de>"; | ||||
|  | ||||
|   description | ||||
|    "This module contains a collection of generally useful derived | ||||
|     YANG data types. | ||||
|  | ||||
|     Copyright (c) 2013 IETF Trust and the persons identified as | ||||
|     authors of the code.  All rights reserved. | ||||
|  | ||||
|     Redistribution and use in source and binary forms, with or | ||||
|     without modification, is permitted pursuant to, and subject | ||||
|     to the license terms contained in, the Simplified BSD License | ||||
|     set forth in Section 4.c of the IETF Trust's Legal Provisions | ||||
|     Relating to IETF Documents | ||||
|     (http://trustee.ietf.org/license-info). | ||||
|  | ||||
|     This version of this YANG module is part of RFC 6991; see | ||||
|     the RFC itself for full legal notices."; | ||||
|  | ||||
|   revision 2013-07-15 { | ||||
|     description | ||||
|      "This revision adds the following new data types: | ||||
|       - yang-identifier | ||||
|       - hex-string | ||||
|       - uuid | ||||
|       - dotted-quad"; | ||||
|     reference | ||||
|      "RFC 6991: Common YANG Data Types"; | ||||
|   } | ||||
|  | ||||
|   revision 2010-09-24 { | ||||
|     description | ||||
|      "Initial revision."; | ||||
|     reference | ||||
|      "RFC 6021: Common YANG Data Types"; | ||||
|   } | ||||
|  | ||||
|   /*** collection of counter and gauge types ***/ | ||||
|  | ||||
|   typedef counter32 { | ||||
|     type uint32; | ||||
|     description | ||||
|      "The counter32 type represents a non-negative integer | ||||
|       that monotonically increases until it reaches a | ||||
|       maximum value of 2^32-1 (4294967295 decimal), when it | ||||
|       wraps around and starts increasing again from zero. | ||||
|  | ||||
|       Counters have no defined 'initial' value, and thus, a | ||||
|       single value of a counter has (in general) no information | ||||
|       content.  Discontinuities in the monotonically increasing | ||||
|       value normally occur at re-initialization of the | ||||
|       management system, and at other times as specified in the | ||||
|       description of a schema node using this type.  If such | ||||
|       other times can occur, for example, the creation of | ||||
|       a schema node of type counter32 at times other than | ||||
|       re-initialization, then a corresponding schema node | ||||
|       should be defined, with an appropriate type, to indicate | ||||
|       the last discontinuity. | ||||
|  | ||||
|       The counter32 type should not be used for configuration | ||||
|       schema nodes.  A default statement SHOULD NOT be used in | ||||
|       combination with the type counter32. | ||||
|  | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the Counter32 type of the SMIv2."; | ||||
|     reference | ||||
|      "RFC 2578: Structure of Management Information Version 2 | ||||
|                 (SMIv2)"; | ||||
|   } | ||||
|  | ||||
|   typedef zero-based-counter32 { | ||||
|     type yang:counter32; | ||||
|     default "0"; | ||||
|     description | ||||
|      "The zero-based-counter32 type represents a counter32 | ||||
|       that has the defined 'initial' value zero. | ||||
|  | ||||
|       A schema node of this type will be set to zero (0) on creation | ||||
|       and will thereafter increase monotonically until it reaches | ||||
|       a maximum value of 2^32-1 (4294967295 decimal), when it | ||||
|       wraps around and starts increasing again from zero. | ||||
|  | ||||
|       Provided that an application discovers a new schema node | ||||
|       of this type within the minimum time to wrap, it can use the | ||||
|       'initial' value as a delta.  It is important for a management | ||||
|       station to be aware of this minimum time and the actual time | ||||
|       between polls, and to discard data if the actual time is too | ||||
|       long or there is no defined minimum time. | ||||
|  | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the ZeroBasedCounter32 textual convention of the SMIv2."; | ||||
|     reference | ||||
|       "RFC 4502: Remote Network Monitoring Management Information | ||||
|                  Base Version 2"; | ||||
|   } | ||||
|  | ||||
|   typedef counter64 { | ||||
|     type uint64; | ||||
|     description | ||||
|      "The counter64 type represents a non-negative integer | ||||
|       that monotonically increases until it reaches a | ||||
|       maximum value of 2^64-1 (18446744073709551615 decimal), | ||||
|       when it wraps around and starts increasing again from zero. | ||||
|  | ||||
|       Counters have no defined 'initial' value, and thus, a | ||||
|       single value of a counter has (in general) no information | ||||
|       content.  Discontinuities in the monotonically increasing | ||||
|       value normally occur at re-initialization of the | ||||
|       management system, and at other times as specified in the | ||||
|       description of a schema node using this type.  If such | ||||
|       other times can occur, for example, the creation of | ||||
|       a schema node of type counter64 at times other than | ||||
|       re-initialization, then a corresponding schema node | ||||
|       should be defined, with an appropriate type, to indicate | ||||
|       the last discontinuity. | ||||
|  | ||||
|       The counter64 type should not be used for configuration | ||||
|       schema nodes.  A default statement SHOULD NOT be used in | ||||
|       combination with the type counter64. | ||||
|  | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the Counter64 type of the SMIv2."; | ||||
|     reference | ||||
|      "RFC 2578: Structure of Management Information Version 2 | ||||
|                 (SMIv2)"; | ||||
|   } | ||||
|  | ||||
|   typedef zero-based-counter64 { | ||||
|     type yang:counter64; | ||||
|     default "0"; | ||||
|     description | ||||
|      "The zero-based-counter64 type represents a counter64 that | ||||
|       has the defined 'initial' value zero. | ||||
|  | ||||
|       A schema node of this type will be set to zero (0) on creation | ||||
|       and will thereafter increase monotonically until it reaches | ||||
|       a maximum value of 2^64-1 (18446744073709551615 decimal), | ||||
|       when it wraps around and starts increasing again from zero. | ||||
|  | ||||
|       Provided that an application discovers a new schema node | ||||
|       of this type within the minimum time to wrap, it can use the | ||||
|       'initial' value as a delta.  It is important for a management | ||||
|       station to be aware of this minimum time and the actual time | ||||
|       between polls, and to discard data if the actual time is too | ||||
|       long or there is no defined minimum time. | ||||
|  | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the ZeroBasedCounter64 textual convention of the SMIv2."; | ||||
|     reference | ||||
|      "RFC 2856: Textual Conventions for Additional High Capacity | ||||
|                 Data Types"; | ||||
|   } | ||||
|  | ||||
|   typedef gauge32 { | ||||
|     type uint32; | ||||
|     description | ||||
|      "The gauge32 type represents a non-negative integer, which | ||||
|       may increase or decrease, but shall never exceed a maximum | ||||
|       value, nor fall below a minimum value.  The maximum value | ||||
|       cannot be greater than 2^32-1 (4294967295 decimal), and | ||||
|       the minimum value cannot be smaller than 0.  The value of | ||||
|       a gauge32 has its maximum value whenever the information | ||||
|       being modeled is greater than or equal to its maximum | ||||
|       value, and has its minimum value whenever the information | ||||
|       being modeled is smaller than or equal to its minimum value. | ||||
|       If the information being modeled subsequently decreases | ||||
|       below (increases above) the maximum (minimum) value, the | ||||
|       gauge32 also decreases (increases). | ||||
|  | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the Gauge32 type of the SMIv2."; | ||||
|     reference | ||||
|      "RFC 2578: Structure of Management Information Version 2 | ||||
|                 (SMIv2)"; | ||||
|   } | ||||
|  | ||||
|   typedef gauge64 { | ||||
|     type uint64; | ||||
|     description | ||||
|      "The gauge64 type represents a non-negative integer, which | ||||
|       may increase or decrease, but shall never exceed a maximum | ||||
|       value, nor fall below a minimum value.  The maximum value | ||||
|       cannot be greater than 2^64-1 (18446744073709551615), and | ||||
|       the minimum value cannot be smaller than 0.  The value of | ||||
|       a gauge64 has its maximum value whenever the information | ||||
|       being modeled is greater than or equal to its maximum | ||||
|       value, and has its minimum value whenever the information | ||||
|       being modeled is smaller than or equal to its minimum value. | ||||
|       If the information being modeled subsequently decreases | ||||
|       below (increases above) the maximum (minimum) value, the | ||||
|       gauge64 also decreases (increases). | ||||
|  | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the CounterBasedGauge64 SMIv2 textual convention defined | ||||
|       in RFC 2856"; | ||||
|     reference | ||||
|      "RFC 2856: Textual Conventions for Additional High Capacity | ||||
|                 Data Types"; | ||||
|   } | ||||
|  | ||||
|   /*** collection of identifier-related types ***/ | ||||
|  | ||||
|   typedef object-identifier { | ||||
|     type string { | ||||
|       pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' | ||||
|             + '(\.(0|([1-9]\d*)))*'; | ||||
|     } | ||||
|     description | ||||
|      "The object-identifier type represents administratively | ||||
|       assigned names in a registration-hierarchical-name tree. | ||||
|  | ||||
|       Values of this type are denoted as a sequence of numerical | ||||
|       non-negative sub-identifier values.  Each sub-identifier | ||||
|       value MUST NOT exceed 2^32-1 (4294967295).  Sub-identifiers | ||||
|       are separated by single dots and without any intermediate | ||||
|       whitespace. | ||||
|  | ||||
|       The ASN.1 standard restricts the value space of the first | ||||
|       sub-identifier to 0, 1, or 2.  Furthermore, the value space | ||||
|       of the second sub-identifier is restricted to the range | ||||
|       0 to 39 if the first sub-identifier is 0 or 1.  Finally, | ||||
|       the ASN.1 standard requires that an object identifier | ||||
|       has always at least two sub-identifiers.  The pattern | ||||
|       captures these restrictions. | ||||
|  | ||||
|       Although the number of sub-identifiers is not limited, | ||||
|       module designers should realize that there may be | ||||
|       implementations that stick with the SMIv2 limit of 128 | ||||
|       sub-identifiers. | ||||
|  | ||||
|       This type is a superset of the SMIv2 OBJECT IDENTIFIER type | ||||
|       since it is not restricted to 128 sub-identifiers.  Hence, | ||||
|       this type SHOULD NOT be used to represent the SMIv2 OBJECT | ||||
|       IDENTIFIER type; the object-identifier-128 type SHOULD be | ||||
|       used instead."; | ||||
|     reference | ||||
|      "ISO9834-1: Information technology -- Open Systems | ||||
|       Interconnection -- Procedures for the operation of OSI | ||||
|       Registration Authorities: General procedures and top | ||||
|       arcs of the ASN.1 Object Identifier tree"; | ||||
|   } | ||||
|  | ||||
|   typedef object-identifier-128 { | ||||
|     type object-identifier { | ||||
|       pattern '\d*(\.\d*){1,127}'; | ||||
|     } | ||||
|     description | ||||
|      "This type represents object-identifiers restricted to 128 | ||||
|       sub-identifiers. | ||||
|  | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the OBJECT IDENTIFIER type of the SMIv2."; | ||||
|     reference | ||||
|      "RFC 2578: Structure of Management Information Version 2 | ||||
|                 (SMIv2)"; | ||||
|   } | ||||
|  | ||||
|   typedef yang-identifier { | ||||
|     type string { | ||||
|       length "1..max"; | ||||
|       pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*'; | ||||
|       pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*'; | ||||
|     } | ||||
|     description | ||||
|       "A YANG identifier string as defined by the 'identifier' | ||||
|        rule in Section 12 of RFC 6020.  An identifier must | ||||
|        start with an alphabetic character or an underscore | ||||
|        followed by an arbitrary sequence of alphabetic or | ||||
|        numeric characters, underscores, hyphens, or dots. | ||||
|  | ||||
|        A YANG identifier MUST NOT start with any possible | ||||
|        combination of the lowercase or uppercase character | ||||
|        sequence 'xml'."; | ||||
|     reference | ||||
|       "RFC 6020: YANG - A Data Modeling Language for the Network | ||||
|                  Configuration Protocol (NETCONF)"; | ||||
|   } | ||||
|  | ||||
|   /*** collection of types related to date and time***/ | ||||
|  | ||||
|   typedef date-and-time { | ||||
|     type string { | ||||
|       pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' | ||||
|             + '(Z|[\+\-]\d{2}:\d{2})'; | ||||
|     } | ||||
|     description | ||||
|      "The date-and-time type is a profile of the ISO 8601 | ||||
|       standard for representation of dates and times using the | ||||
|       Gregorian calendar.  The profile is defined by the | ||||
|       date-time production in Section 5.6 of RFC 3339. | ||||
|  | ||||
|       The date-and-time type is compatible with the dateTime XML | ||||
|       schema type with the following notable exceptions: | ||||
|  | ||||
|       (a) The date-and-time type does not allow negative years. | ||||
|  | ||||
|       (b) The date-and-time time-offset -00:00 indicates an unknown | ||||
|           time zone (see RFC 3339) while -00:00 and +00:00 and Z | ||||
|           all represent the same time zone in dateTime. | ||||
|  | ||||
|       (c) The canonical format (see below) of data-and-time values | ||||
|           differs from the canonical format used by the dateTime XML | ||||
|           schema type, which requires all times to be in UTC using | ||||
|           the time-offset 'Z'. | ||||
|  | ||||
|       This type is not equivalent to the DateAndTime textual | ||||
|       convention of the SMIv2 since RFC 3339 uses a different | ||||
|       separator between full-date and full-time and provides | ||||
|       higher resolution of time-secfrac. | ||||
|  | ||||
|       The canonical format for date-and-time values with a known time | ||||
|       zone uses a numeric time zone offset that is calculated using | ||||
|       the device's configured known offset to UTC time.  A change of | ||||
|       the device's offset to UTC time will cause date-and-time values | ||||
|       to change accordingly.  Such changes might happen periodically | ||||
|       in case a server follows automatically daylight saving time | ||||
|       (DST) time zone offset changes.  The canonical format for | ||||
|       date-and-time values with an unknown time zone (usually | ||||
|       referring to the notion of local time) uses the time-offset | ||||
|       -00:00."; | ||||
|     reference | ||||
|      "RFC 3339: Date and Time on the Internet: Timestamps | ||||
|       RFC 2579: Textual Conventions for SMIv2 | ||||
|       XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; | ||||
|   } | ||||
|  | ||||
|   typedef timeticks { | ||||
|     type uint32; | ||||
|     description | ||||
|      "The timeticks type represents a non-negative integer that | ||||
|       represents the time, modulo 2^32 (4294967296 decimal), in | ||||
|       hundredths of a second between two epochs.  When a schema | ||||
|       node is defined that uses this type, the description of | ||||
|       the schema node identifies both of the reference epochs. | ||||
|  | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the TimeTicks type of the SMIv2."; | ||||
|     reference | ||||
|      "RFC 2578: Structure of Management Information Version 2 | ||||
|                 (SMIv2)"; | ||||
|   } | ||||
|  | ||||
|   typedef timestamp { | ||||
|     type yang:timeticks; | ||||
|     description | ||||
|      "The timestamp type represents the value of an associated | ||||
|       timeticks schema node at which a specific occurrence | ||||
|       happened.  The specific occurrence must be defined in the | ||||
|       description of any schema node defined using this type.  When | ||||
|       the specific occurrence occurred prior to the last time the | ||||
|       associated timeticks attribute was zero, then the timestamp | ||||
|       value is zero.  Note that this requires all timestamp values | ||||
|       to be reset to zero when the value of the associated timeticks | ||||
|       attribute reaches 497+ days and wraps around to zero. | ||||
|  | ||||
|       The associated timeticks schema node must be specified | ||||
|       in the description of any schema node using this type. | ||||
|  | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the TimeStamp textual convention of the SMIv2."; | ||||
|     reference | ||||
|      "RFC 2579: Textual Conventions for SMIv2"; | ||||
|   } | ||||
|  | ||||
|   /*** collection of generic address types ***/ | ||||
|  | ||||
|   typedef phys-address { | ||||
|     type string { | ||||
|       pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; | ||||
|     } | ||||
|  | ||||
|     description | ||||
|      "Represents media- or physical-level addresses represented | ||||
|       as a sequence octets, each octet represented by two hexadecimal | ||||
|       numbers.  Octets are separated by colons.  The canonical | ||||
|       representation uses lowercase characters. | ||||
|  | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the PhysAddress textual convention of the SMIv2."; | ||||
|     reference | ||||
|      "RFC 2579: Textual Conventions for SMIv2"; | ||||
|   } | ||||
|  | ||||
|   typedef mac-address { | ||||
|     type string { | ||||
|       pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; | ||||
|     } | ||||
|     description | ||||
|      "The mac-address type represents an IEEE 802 MAC address. | ||||
|       The canonical representation uses lowercase characters. | ||||
|  | ||||
|       In the value set and its semantics, this type is equivalent | ||||
|       to the MacAddress textual convention of the SMIv2."; | ||||
|     reference | ||||
|      "IEEE 802: IEEE Standard for Local and Metropolitan Area | ||||
|                 Networks: Overview and Architecture | ||||
|       RFC 2579: Textual Conventions for SMIv2"; | ||||
|   } | ||||
|  | ||||
|   /*** collection of XML-specific types ***/ | ||||
|  | ||||
|   typedef xpath1.0 { | ||||
|     type string; | ||||
|     description | ||||
|      "This type represents an XPATH 1.0 expression. | ||||
|  | ||||
|       When a schema node is defined that uses this type, the | ||||
|       description of the schema node MUST specify the XPath | ||||
|       context in which the XPath expression is evaluated."; | ||||
|     reference | ||||
|      "XPATH: XML Path Language (XPath) Version 1.0"; | ||||
|   } | ||||
|  | ||||
|   /*** collection of string types ***/ | ||||
|  | ||||
|   typedef hex-string { | ||||
|     type string { | ||||
|       pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; | ||||
|     } | ||||
|     description | ||||
|      "A hexadecimal string with octets represented as hex digits | ||||
|       separated by colons.  The canonical representation uses | ||||
|       lowercase characters."; | ||||
|   } | ||||
|  | ||||
|   typedef uuid { | ||||
|     type string { | ||||
|       pattern '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-' | ||||
|             + '[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'; | ||||
|     } | ||||
|     description | ||||
|      "A Universally Unique IDentifier in the string representation | ||||
|       defined in RFC 4122.  The canonical representation uses | ||||
|       lowercase characters. | ||||
|  | ||||
|       The following is an example of a UUID in string representation: | ||||
|       f81d4fae-7dec-11d0-a765-00a0c91e6bf6 | ||||
|       "; | ||||
|     reference | ||||
|      "RFC 4122: A Universally Unique IDentifier (UUID) URN | ||||
|                 Namespace"; | ||||
|   } | ||||
|  | ||||
|   typedef dotted-quad { | ||||
|     type string { | ||||
|       pattern | ||||
|         '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' | ||||
|       + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'; | ||||
|     } | ||||
|     description | ||||
|       "An unsigned 32-bit number expressed in the dotted-quad | ||||
|        notation, i.e., four octets written as decimal numbers | ||||
|        and separated with the '.' (full stop) character."; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										722
									
								
								gnpy/yang/io.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										722
									
								
								gnpy/yang/io.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,722 @@ | ||||
| # SPDX-License-Identifier: BSD-3-Clause | ||||
| # | ||||
| # Copyright (C) 2020 Telecom Infra Project and GNPy contributors | ||||
| # see LICENSE.md for a list of contributors | ||||
| # | ||||
|  | ||||
| """ | ||||
| Reading and writing YANG data | ||||
| ============================= | ||||
|  | ||||
| Module :py:mod:`gnpy.yang.io` enables loading of data that are formatted according to the YANG+JSON rules. | ||||
| Use :func:`load_from_yang` to parse and validate the data, and :func:`save_equipment` to store the equipment library. | ||||
| """ | ||||
|  | ||||
| from networkx import DiGraph | ||||
| from typing import Any, Dict, List, Tuple, Union | ||||
| import numpy as np | ||||
| import yangson as _y | ||||
| import copy | ||||
| from gnpy.core import elements, exceptions | ||||
| import gnpy.tools.json_io as _ji | ||||
| import gnpy.core.science_utils as _sci | ||||
| import gnpy.yang | ||||
| import gnpy.yang.conversion as _conv | ||||
|  | ||||
|  | ||||
| def create_datamodel() -> _y.DataModel: | ||||
|     '''Create a new yangson.DataModel''' | ||||
|     return _y.DataModel.from_file(gnpy.yang._yang_library(), (gnpy.yang.external_path(), gnpy.yang.model_path())) | ||||
|  | ||||
|  | ||||
| def _extract_common_fiber(fiber: _y.instance.ArrayEntry) -> Dict: | ||||
|     return { | ||||
|         'dispersion': float(fiber['chromatic-dispersion'].value) * _conv.FIBER_DISPERSION, | ||||
|         'dispersion_slope': float(fiber['chromatic-dispersion-slope'].value) * _conv.FIBER_DISPERSION_SLOPE, | ||||
|         'gamma': float(fiber['gamma'].value) * _conv.FIBER_GAMMA, | ||||
|         'pmd_coef': float(fiber['pmd-coefficient'].value) * _conv.FIBER_PMD_COEF, | ||||
|     } | ||||
|  | ||||
|  | ||||
| def _transform_fiber(fiber: _y.instance.ArrayEntry) -> _ji.Fiber: | ||||
|     '''Turn yangson's ``tip-photonic-equipment:fiber`` into a Fiber equipment type representation''' | ||||
|     return _ji.Fiber( | ||||
|         type_variety=fiber['type'].value, | ||||
|         **_extract_common_fiber(fiber), | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def _transform_raman_fiber(fiber: _y.instance.ArrayEntry) -> _ji.RamanFiber: | ||||
|     '''Turn yangson's ``tip-photonic-equipment:fiber`` with a Raman section into a RamanFiber equipment type representation''' | ||||
|     return _ji.RamanFiber( | ||||
|         type_variety=fiber['type'].value, | ||||
|         raman_efficiency={  # FIXME: check the order here, the existing code is picky, and YANG doesn't guarantee any particular order here | ||||
|             'cr': [x['cr'].value for x in fiber['raman-efficiency']], | ||||
|             'frequency_offset': [float(x['delta-frequency'].value) for x in fiber['raman-efficiency']], | ||||
|         }, | ||||
|         **_extract_common_fiber(fiber), | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def _extract_per_spectrum(key: str, yang) -> List[float]: | ||||
|     '''Extract per-frequency offsets from a freq->offset YANG list and store them as a list interpolated at a 50 GHz grid''' | ||||
|     if key not in yang: | ||||
|         return (0, ) | ||||
|     data = [(int(x['frequency'].value), float(x[key].value)) for x in yang[key]] | ||||
|     data.sort(key=lambda tup: tup[0]) | ||||
|  | ||||
|     # FIXME: move this to gnpy.core.elements | ||||
|     # FIXME: we're also probably doing the interpolation wrong in elements.py (C-band grid vs. actual carrier frequencies) | ||||
|     keys = [x[0] for x in data] | ||||
|     values = [x[1] for x in data] | ||||
|     frequencies = [int(191.3e12 + channel * 50e9) for channel in range(96)] | ||||
|     data = [x for x in np.interp(frequencies, keys, values)]  # force back Python's native list to silence a FutureWarning: elementwise comparison failed | ||||
|     return data | ||||
|  | ||||
|  | ||||
| def _transform_edfa(edfa: _y.instance.ArrayEntry) -> _ji.Amp: | ||||
|     '''Turn yangson's ``tip-photonic-equipment:amplifier`` into an EDFA equipment type representation''' | ||||
|  | ||||
|     POLYNOMIAL_NF = 'polynomial-NF' | ||||
|     OPENROADM_ILA = 'OpenROADM-ILA' | ||||
|     OPENROADM_PREAMP = 'OpenROADM-preamp' | ||||
|     OPENROADM_BOOSTER = 'OpenROADM-booster' | ||||
|     MIN_MAX_NF = 'min-max-NF' | ||||
|     COMPOSITE = 'composite' | ||||
|     RAMAN_APPROX = 'raman-approximation' | ||||
|     GAIN_RIPPLE = 'gain-ripple' | ||||
|     NF_RIPPLE = 'nf-ripple' | ||||
|     DYNAMIC_GAIN_TILT = 'dynamic-gain-tilt' | ||||
|  | ||||
|     name = edfa['type'].value | ||||
|     type_def = None | ||||
|     nf_model = None | ||||
|     dual_stage_model = None | ||||
|     f_min = None | ||||
|     f_max = None | ||||
|     gain_flatmax = None | ||||
|     p_max = None | ||||
|     nf_fit_coeff = None | ||||
|     nf_ripple = None | ||||
|     dgt = None | ||||
|     gain_ripple = None | ||||
|  | ||||
|     if COMPOSITE in edfa: | ||||
|         # this model will be postprocessed in _fixup_dual_stage, so just save some placeholders here | ||||
|         model = edfa[COMPOSITE] | ||||
|         type_def = 'dual_stage' | ||||
|         dual_stage_model = _ji.Model_dual_stage(model['preamp'].value, model['booster'].value) | ||||
|     else: | ||||
|         if POLYNOMIAL_NF in edfa: | ||||
|             model = edfa[POLYNOMIAL_NF] | ||||
|             nf_fit_coeff = (float(model['a'].value), float(model['b'].value), float(model['c'].value), float(model['d'].value)) | ||||
|             type_def = 'advanced_model' | ||||
|         elif OPENROADM_ILA in edfa: | ||||
|             model = edfa[OPENROADM_ILA] | ||||
|             nf_model = _ji.Model_openroadm_ila(nf_coef=(float(model['a'].value), float(model['b'].value), | ||||
|                                                         float(model['c'].value), float(model['d'].value))) | ||||
|             type_def = 'openroadm' | ||||
|         elif OPENROADM_PREAMP in edfa: | ||||
|             type_def = 'openroadm_preamp' | ||||
|         elif OPENROADM_BOOSTER in edfa: | ||||
|             type_def = 'openroadm_booster' | ||||
|         elif MIN_MAX_NF in edfa: | ||||
|             model = edfa[MIN_MAX_NF] | ||||
|             nf_min = float(model['nf-min'].value) | ||||
|             nf_max = float(model['nf-max'].value) | ||||
|             nf1, nf2, delta_p = _sci.estimate_nf_model(name, float(edfa['gain-min'].value), float(edfa['gain-flatmax'].value), | ||||
|                                                        nf_min, nf_max) | ||||
|             nf_model = _ji.Model_vg(nf1, nf2, delta_p, nf_min, nf_max) | ||||
|             type_def = 'variable_gain' | ||||
|         elif RAMAN_APPROX in edfa: | ||||
|             model = edfa[RAMAN_APPROX] | ||||
|             nf_fit_coeff = (0., 0., 0., float(model['nf'].value)) | ||||
|             type_def = 'advanced_model' | ||||
|         else: | ||||
|             raise NotImplementedError(f'Internal error: EDFA model {name}: unrecognized amplifier NF model for EDFA. ' | ||||
|                                       'Error in the YANG validation code.') | ||||
|  | ||||
|         gain_flatmax = float(edfa['gain-flatmax'].value) | ||||
|         f_min = float(edfa['frequency-min'].value) * _conv.THZ | ||||
|         f_max = float(edfa['frequency-max'].value) * _conv.THZ | ||||
|         p_max = float(edfa['max-power-out'].value) | ||||
|  | ||||
|         gain_ripple = _extract_per_spectrum(GAIN_RIPPLE, edfa) | ||||
|         dgt = _extract_per_spectrum(DYNAMIC_GAIN_TILT, edfa) | ||||
|         nf_ripple = _extract_per_spectrum(NF_RIPPLE, edfa) | ||||
|  | ||||
|     return _ji.Amp( | ||||
|         type_variety=name, | ||||
|         type_def=type_def, | ||||
|         f_min=f_min, | ||||
|         f_max=f_max, | ||||
|         gain_min=float(edfa['gain-min'].value), | ||||
|         gain_flatmax=gain_flatmax, | ||||
|         p_max=p_max, | ||||
|         nf_fit_coeff=nf_fit_coeff, | ||||
|         nf_ripple=nf_ripple, | ||||
|         dgt=dgt, | ||||
|         gain_ripple=gain_ripple, | ||||
|         out_voa_auto=None,  # FIXME | ||||
|         allowed_for_design=True,  # FIXME | ||||
|         raman=False, | ||||
|         nf_model=nf_model, | ||||
|         dual_stage_model=dual_stage_model, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def _fixup_dual_stage(amps: Dict[str, _ji.Amp]) -> Dict[str, _ji.Amp]: | ||||
|     '''Replace preamp/booster string model IDs with references to actual objects''' | ||||
|     for name, amp in amps.items(): | ||||
|         if amp.dual_stage_model is None: | ||||
|             continue | ||||
|         preamp = amps[amp.dual_stage_model.preamp_variety] | ||||
|         booster = amps[amp.dual_stage_model.booster_variety] | ||||
|         this_amp = amps[name] | ||||
|         # FIXME: the old JSON code copies each and every attr, do we need that here? | ||||
|         for attr in preamp.__dict__.keys(): | ||||
|             setattr(this_amp, f'preamp_{attr}', getattr(preamp, attr)) | ||||
|         for attr in booster.__dict__.keys(): | ||||
|             setattr(this_amp, f'booster_{attr}', getattr(booster, attr)) | ||||
|     return amps | ||||
|  | ||||
|  | ||||
| def _transform_roadm(roadm: _y.instance.ArrayEntry) -> _ji.Roadm: | ||||
|     '''Turn yangson's ``tip-photonic-equipment:roadm`` into a ROADM equipment type representation''' | ||||
|     return _ji.Roadm( | ||||
|         target_pch_out_db=float(roadm['target-channel-out-power'].value), | ||||
|         add_drop_osnr=float(roadm['add-drop-osnr'].value), | ||||
|         pmd=float(roadm['polarization-mode-dispersion'].value), | ||||
|         restrictions={ | ||||
|             'preamp_variety_list': [amp.value for amp in roadm['compatible-preamp']] if 'compatible-preamp' in roadm else [], | ||||
|             'booster_variety_list': [amp.value for amp in roadm['compatible-booster']] if 'compatible-booster' in roadm else [], | ||||
|         }, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def _transform_transceiver_mode(mode: _y.instance.ArrayEntry) -> Dict[str, object]: | ||||
|     return { | ||||
|         'format': mode['name'].value, | ||||
|         'baud_rate': float(mode['baud-rate'].value) * _conv.GIGA, | ||||
|         'OSNR': float(mode['required-osnr'].value), | ||||
|         'bit_rate': float(mode['bit-rate'].value) * _conv.GIGA, | ||||
|         'roll_off': float(mode['tx-roll-off'].value), | ||||
|         'tx_osnr': float(mode['in-band-tx-osnr'].value), | ||||
|         'min_spacing': float(mode['grid-spacing'].value) * _conv.GIGA, | ||||
|         'cost': float(mode['tip-photonic-simulation:cost'].value), | ||||
|     } | ||||
|  | ||||
|  | ||||
| def _transform_transceiver(txp: _y.instance.ArrayEntry) -> _ji.Transceiver: | ||||
|     '''Turn yangson's ``tip-photonic-equipment:transceiver`` into a Transceiver equipment type representation''' | ||||
|     return _ji.Transceiver( | ||||
|         type_variety=txp['type'].value, | ||||
|         frequency={ | ||||
|             "min": float(txp['frequency-min'].value) * _conv.THZ, | ||||
|             "max": float(txp['frequency-max'].value) * _conv.THZ, | ||||
|         }, | ||||
|         mode=[_transform_transceiver_mode(mode) for mode in txp['mode']], | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def _optional_float(yangish, key, default=None): | ||||
|     '''Retrieve a decimal64 value as a float, or None if not present''' | ||||
|     return float(yangish[key].value) if key in yangish else default | ||||
|  | ||||
|  | ||||
| def _load_equipment(data: _y.instance.RootNode, sim_data: _y.instance.InstanceNode) -> Dict[str, Dict[str, Any]]: | ||||
|     '''Load the equipment library from YANG data''' | ||||
|     equipment = { | ||||
|         'Edfa': _fixup_dual_stage({x['type'].value: _transform_edfa(x) for x in data['tip-photonic-equipment:amplifier']}), | ||||
|         'Fiber': {x['type'].value: _transform_fiber(x) for x in data['tip-photonic-equipment:fiber']}, | ||||
|         'RamanFiber': {x['type'].value: _transform_raman_fiber(x) for x in data['tip-photonic-equipment:fiber'] if 'raman-efficiency' in x}, | ||||
|         'Span': {'default': _ji.Span( | ||||
|             power_mode='power-mode' in sim_data['autodesign'], | ||||
|             delta_power_range_db=[ | ||||
|                 float(sim_data['autodesign']['power-adjustment-for-span-loss']['maximal-reduction'].value), | ||||
|                 float(sim_data['autodesign']['power-adjustment-for-span-loss']['maximal-boost'].value), | ||||
|                 float(sim_data['autodesign']['power-adjustment-for-span-loss']['excursion-step-size'].value), | ||||
|             ], | ||||
|             max_fiber_lineic_loss_for_raman=0,  # FIXME: can we deprecate this? | ||||
|             target_extended_gain=2.5,  # FIXME | ||||
|             max_length=150,  # FIXME | ||||
|             length_units='km',  # FIXME | ||||
|             max_loss=None,  # FIXME | ||||
|             padding=0,  # FIXME | ||||
|             EOL=0,  # FIXME | ||||
|             con_in=0, | ||||
|             con_out=0, | ||||
|         ) | ||||
|         }, | ||||
|         'Roadm': {x['type'].value: _transform_roadm(x) for x in data['tip-photonic-equipment:roadm']}, | ||||
|         'SI': { | ||||
|             'default': _ji.SI( | ||||
|                 f_min=float(sim_data['grid']['frequency-min'].value) * _conv.THZ, | ||||
|                 f_max=float(sim_data['grid']['frequency-max'].value) * _conv.THZ, | ||||
|                 baud_rate=float(sim_data['grid']['baud-rate'].value) * _conv.GIGA, | ||||
|                 spacing=float(sim_data['grid']['spacing'].value) * _conv.GIGA, | ||||
|                 power_dbm=float(sim_data['grid']['power'].value), | ||||
|                 power_range_db=( | ||||
|                     [ # start, stop, step | ||||
|                         float(sim_data['autodesign']['power-mode']['power-sweep']['start'].value), | ||||
|                         float(sim_data['autodesign']['power-mode']['power-sweep']['stop'].value), | ||||
|                         float(sim_data['autodesign']['power-mode']['power-sweep']['step-size'].value), | ||||
|                     ] if 'power-sweep' in sim_data['autodesign']['power-mode'] else [0, 0, 0] | ||||
|                 ) if ('power-mode' in sim_data['autodesign']) else None, | ||||
|                 roll_off=float(sim_data['grid']['tx-roll-off'].value), | ||||
|                 sys_margins=float(sim_data['system-margin'].value), | ||||
|                 tx_osnr=float(sim_data['grid']['tx-osnr'].value), | ||||
|             ), | ||||
|         }, | ||||
|         'Transceiver': {x['type'].value: _transform_transceiver(x) for x in data['tip-photonic-equipment:transceiver']}, | ||||
|     } | ||||
|     return equipment | ||||
|  | ||||
|  | ||||
| def _load_network(data: _y.instance.RootNode, equipment: Dict[str, Dict[str, Any]]) -> DiGraph: | ||||
|     '''Load the network topology from YANG data''' | ||||
|  | ||||
|     network = DiGraph() | ||||
|     nodes = {} | ||||
|     for net in data['ietf-network:networks']['ietf-network:network']: | ||||
|         if 'network-types' not in net: | ||||
|             continue | ||||
|         if 'tip-photonic-topology:photonic-topology' not in net['network-types']: | ||||
|             continue | ||||
|         for node in net['ietf-network:node']: | ||||
|             uid = node['node-id'].value | ||||
|             location = None | ||||
|             if 'tip-photonic-topology:geo-location' in node: | ||||
|                 loc = node['tip-photonic-topology:geo-location'] | ||||
|                 if 'x' in loc and 'y' in loc: | ||||
|                     location = elements.Location( | ||||
|                         longitude=float(loc['tip-photonic-topology:x'].value), | ||||
|                         latitude=float(loc['tip-photonic-topology:y'].value) | ||||
|                     ) | ||||
|             metadata = {'location': location} if location is not None else None | ||||
|  | ||||
|             if 'tip-photonic-topology:amplifier' in node: | ||||
|                 amp = node['tip-photonic-topology:amplifier'] | ||||
|                 type_variety = amp['model'].value | ||||
|                 params = copy.copy(equipment['Edfa'][type_variety].__dict__) | ||||
|                 el = elements.Edfa( | ||||
|                     uid=uid, | ||||
|                     type_variety=type_variety, | ||||
|                     params=params, | ||||
|                     metadata=metadata, | ||||
|                     operational={ | ||||
|                         'gain_target': _optional_float(amp, 'gain-target'), | ||||
|                         'tilt_target': _optional_float(amp, 'tilt-target', 0), | ||||
|                         'out_voa': _optional_float(amp, 'out-voa-target'), | ||||
|                         'delta_p': _optional_float(amp, 'delta-p'), | ||||
|                     }, | ||||
|                 ) | ||||
|             elif 'tip-photonic-topology:roadm' in node: | ||||
|                 roadm = node['tip-photonic-topology:roadm'] | ||||
|                 type_variety = roadm['model'].value | ||||
|                 params = copy.copy(equipment['Roadm'][type_variety].__dict__) | ||||
|                 el = elements.Roadm( | ||||
|                     uid=uid, | ||||
|                     type_variety=roadm['model'].value, | ||||
|                     metadata={'location': location} if location is not None else None, | ||||
|                     params=params, | ||||
|                     # FIXME | ||||
|                 ) | ||||
|             elif 'tip-photonic-topology:transceiver' in node: | ||||
|                 txp = node['tip-photonic-topology:transceiver'] | ||||
|                 el = elements.Transceiver( | ||||
|                     uid=uid, | ||||
|                     type_variety=txp['model'].value, | ||||
|                     metadata={'location': location} if location is not None else None, | ||||
|                     # FIXME | ||||
|                 ) | ||||
|             elif 'tip-photonic-topology:attenuator' in node: | ||||
|                 att = node['tip-photonic-topology:attenuator'] | ||||
|                 el = elements.Fused( | ||||
|                     uid=uid, | ||||
|                     params={ | ||||
|                         'loss': _optional_float(att, 'attenuation', None), | ||||
|                     } | ||||
|                 ) | ||||
|             else: | ||||
|                 raise ValueError(f'Internal error: unrecognized network node {node} which was expected to belong to the photonic-topology') | ||||
|             network.add_node(el) | ||||
|             nodes[el.uid] = el | ||||
|  | ||||
|         # start by creating GNPy network nodes | ||||
|         for link in net['ietf-network-topology:link']: | ||||
|             source = link['source']['source-node'].value | ||||
|             target = link['destination']['dest-node'].value | ||||
|             if 'tip-photonic-topology:fiber' in link: | ||||
|                 fiber = link['tip-photonic-topology:fiber'] | ||||
|                 params = { | ||||
|                     'length_units': 'km',  # FIXME | ||||
|                     'length': float(fiber['length'].value), | ||||
|                     'loss_coef': float(fiber['loss-per-km'].value), | ||||
|                     'att_in': float(fiber['attenuation-in'].value), | ||||
|                     'con_in': float(fiber['conn-att-in'].value), | ||||
|                     'con_out': float(fiber['conn-att-out'].value), | ||||
|                 } | ||||
|                 specs = equipment['Fiber'][fiber['type'].value] | ||||
|                 for key in ('dispersion', 'gamma', 'pmd_coef'): | ||||
|                     params[key] = getattr(specs, key) | ||||
|                 location = elements.Location( | ||||
|                     latitude=(nodes[source].metadata['location'].latitude + nodes[target].metadata['location'].latitude) / 2, | ||||
|                     longitude=(nodes[source].metadata['location'].longitude + nodes[target].metadata['location'].longitude) / 2, | ||||
|                 ) | ||||
|                 el = elements.Fiber( | ||||
|                     uid=link['link-id'].value, | ||||
|                     type_variety=fiber['type'].value, | ||||
|                     params=params, | ||||
|                     metadata={'location': location}, | ||||
|                     # FIXME | ||||
|                 ) | ||||
|                 network.add_node(el) | ||||
|                 nodes[el.uid] = el | ||||
|             elif 'tip-photonic-topology:patch' in link: | ||||
|                 # No GNPy-level node is needed for these | ||||
|                 pass | ||||
|             else: | ||||
|                 raise ValueError(f'Internal error: unrecognized network link {link} which was expected to belong to the photonic-topology') | ||||
|  | ||||
|         # now add actual links | ||||
|         for link in net['ietf-network-topology:link']: | ||||
|             source = link['source']['source-node'].value | ||||
|             target = link['destination']['dest-node'].value | ||||
|             if 'tip-photonic-topology:fiber' in link: | ||||
|                 this_node = link['link-id'].value | ||||
|                 network.add_edge(nodes[source], nodes[this_node], weight=float(fiber['length'].value)) | ||||
|                 network.add_edge(nodes[this_node], nodes[target], weight=0.01) | ||||
|             elif 'tip-photonic-topology:patch' in link: | ||||
|                 network.add_edge(nodes[source], nodes[target], weight=0.01) | ||||
|                 patch = link['tip-photonic-topology:patch'] | ||||
|                 if 'roadm-target-egress-per-channel-power' in patch: | ||||
|                     per_degree_power = float(patch['roadm-target-egress-per-channel-power'].value) | ||||
|                     nodes[source].params.per_degree_pch_out_db[target] = per_degree_power | ||||
|  | ||||
|     # FIXME: read set_egress_amplifier and make it do what I want to do here | ||||
|     # FIXME: be super careful with autodesign!, the assumptions in "legacy JSON" and in "YANG JSON" are very different | ||||
|  | ||||
|     return network | ||||
|  | ||||
|  | ||||
| def load_from_yang(json_data: Dict) -> Tuple[Dict[str, Dict[str, Any]], DiGraph]: | ||||
|     '''Load equipment library, (FIXME: nothing for now, will be the network topology) and simulation options from a YANG-formatted JSON-like object''' | ||||
|     dm = create_datamodel() | ||||
|  | ||||
|     data = dm.from_raw(json_data) | ||||
|     data.validate(ctype=_y.enumerations.ContentType.config) | ||||
|     data = data.add_defaults() | ||||
|     # No warnings are given for "missing data". In YANG, it is either an error if some required data are missing, | ||||
|     # or there are default values which in turn mean that it is safe to not specify those data. There's no middle | ||||
|     # ground like "please yell at me when I missed that, but continue with the simulation". I have to admit I like that. | ||||
|  | ||||
|     SIMULATION = 'tip-photonic-simulation:simulation' | ||||
|     if SIMULATION not in data: | ||||
|         raise exceptions.ConfigurationError(f'YANG data does not contain the /{SIMULATION} element') | ||||
|  | ||||
|     sim_data = data[SIMULATION] | ||||
|     equipment = _load_equipment(data, sim_data) | ||||
|     # FIXME: adjust all Simulation's parameters | ||||
|     network = _load_network(data, equipment) | ||||
|  | ||||
|     return (equipment, network) | ||||
|  | ||||
|  | ||||
| def _store_equipment_edfa(name: str, edfa: _ji.Amp) -> Dict: | ||||
|     '''Save in-memory representation of an EDFA amplifier type into a YANG-formatted dict''' | ||||
|     res = { | ||||
|         'type': name, | ||||
|         'gain-min': str(edfa.gain_min), | ||||
|     } | ||||
|  | ||||
|     if edfa.dual_stage_model is not None: | ||||
|         res['composite'] = { | ||||
|             'preamp': edfa.dual_stage_model.preamp_variety, | ||||
|             'booster': edfa.dual_stage_model.booster_variety, | ||||
|         } | ||||
|     else: | ||||
|         res['frequency-min'] = str(edfa.f_min / _conv.THZ) | ||||
|         res['frequency-max'] = str(edfa.f_max / _conv.THZ) | ||||
|         res['gain-flatmax'] = str(edfa.gain_flatmax) | ||||
|         res['max-power-out'] = str(edfa.p_max) | ||||
|         res['has-output-voa'] = edfa.out_voa_auto | ||||
|  | ||||
|         if isinstance(edfa.nf_model, _ji.Model_fg): | ||||
|             if edfa.nf_model.nf0 < 3: | ||||
|                 res['raman-approximation'] = { | ||||
|                     'nf': str(edfa.nf_model.nf0) | ||||
|                 } | ||||
|             else: | ||||
|                 res['polynomial-NF'] = { | ||||
|                     'a': '0', | ||||
|                     'b': '0', | ||||
|                     'c': '0', | ||||
|                     'd': str(edfa.nf_model.nf0), | ||||
|                 } | ||||
|         elif isinstance(edfa.nf_model, _ji.Model_vg): | ||||
|             res['min-max-NF'] = { | ||||
|                 'nf-min': str(edfa.nf_model.orig_nf_min), | ||||
|                 'nf-max': str(edfa.nf_model.orig_nf_max), | ||||
|             } | ||||
|         elif isinstance(edfa.nf_model, _ji.Model_openroadm_ila): | ||||
|             res['OpenROADM-ILA'] = { | ||||
|                 'a': str(edfa.nf_model.nf_coef[0]), | ||||
|                 'b': str(edfa.nf_model.nf_coef[1]), | ||||
|                 'c': str(edfa.nf_model.nf_coef[2]), | ||||
|                 'd': str(edfa.nf_model.nf_coef[3]), | ||||
|             } | ||||
|         elif isinstance(edfa.nf_model, _ji.Model_openroadm_preamp): | ||||
|             res['OpenROADM-preamp'] = {} | ||||
|         elif isinstance(edfa.nf_model, _ji.Model_openroadm_booster): | ||||
|             res['OpenROADM-booster'] = {} | ||||
|         elif edfa.type_def == 'advanced_model': | ||||
|             res['polynomial-NF'] = { | ||||
|                 'a': str(edfa.nf_fit_coeff[0]), | ||||
|                 'b': str(edfa.nf_fit_coeff[1]), | ||||
|                 'c': str(edfa.nf_fit_coeff[2]), | ||||
|                 'd': str(edfa.nf_fit_coeff[3]), | ||||
|             } | ||||
|  | ||||
|         # FIXME: implement these | ||||
|         # 'nf_ripple': None, | ||||
|         # 'dgt': None, | ||||
|         # 'gain_ripple': None, | ||||
|     return res | ||||
|  | ||||
|  | ||||
| def _store_equipment_fiber(name: str, fiber: Union[_ji.Fiber, _ji.RamanFiber]) -> Dict: | ||||
|     '''Save in-memory representation of a single fiber type into a YANG-formatted dict''' | ||||
|     res = { | ||||
|         'type': name, | ||||
|         'chromatic-dispersion': str(fiber.dispersion / _conv.FIBER_DISPERSION), | ||||
|         'gamma': str(fiber.gamma / _conv.FIBER_GAMMA), | ||||
|         'pmd-coefficient': str(fiber.pmd_coef / _conv.FIBER_PMD_COEF), | ||||
|     } | ||||
|  | ||||
|     # FIXME: do we support setting 'dispersion-slope' via JSON setting in the first place? There are no examples... | ||||
|     try: | ||||
|         res['dispersion-slope'] = str(fiber.dispersion_slope / _conv.FIBER_DISPERSION_SLOPE) | ||||
|     except AttributeError: | ||||
|         pass | ||||
|  | ||||
|     if isinstance(fiber, _ji.RamanFiber): | ||||
|         res['raman-efficiency'] = [ | ||||
|             { | ||||
|                 'delta-frequency': str(freq / _conv.THZ), | ||||
|                 'cr': str(float(cr)), | ||||
|             } for (cr, freq) in zip(fiber.raman_efficiency['cr'], fiber.raman_efficiency['frequency_offset']) | ||||
|         ] | ||||
|  | ||||
|     return res | ||||
|  | ||||
|  | ||||
| def _store_equipment_transceiver(name: str, txp: _ji.Transceiver) -> Dict: | ||||
|     '''Save in-memory representation of a transceiver type into a YANG-formatted dict''' | ||||
|     return { | ||||
|         'type': name, | ||||
|         'frequency-min': str(txp.frequency['min'] / _conv.THZ), | ||||
|         'frequency-max': str(txp.frequency['max'] / _conv.THZ), | ||||
|         'mode': [{ | ||||
|             'name': mode['format'], | ||||
|             'bit-rate': int(mode['bit_rate'] / _conv.GIGA), | ||||
|             'baud-rate': str(float(mode['baud_rate'] / _conv.GIGA)), | ||||
|             'required-osnr': str(float(mode['OSNR'])), | ||||
|             'in-band-tx-osnr': str(float(mode['tx_osnr'])), | ||||
|             'grid-spacing': str(float(mode['min_spacing'] / _conv.GIGA)), | ||||
|             'tx-roll-off': str(float(mode['roll_off'])), | ||||
|             'tip-photonic-simulation:cost': mode['cost'], | ||||
|         } for mode in txp.mode], | ||||
|     } | ||||
|  | ||||
|  | ||||
| def _store_equipment_roadm(name: str, roadm: _ji.Roadm) -> Dict: | ||||
|     '''Save in-memory representation of a ROADM type into a YANG-formatted dict''' | ||||
|     return { | ||||
|         'type': name, | ||||
|         'add-drop-osnr': str(roadm.add_drop_osnr), | ||||
|         'polarization-mode-dispersion': str(roadm.pmd), | ||||
|         'target-channel-out-power': str(roadm.target_pch_out_db), | ||||
|         'compatible-preamp': [amp for amp in roadm.restrictions.get('preamp_variety_list', [])], | ||||
|         'compatible-booster': [amp for amp in roadm.restrictions.get('booster_variety_list', [])], | ||||
|     } | ||||
|  | ||||
|  | ||||
| def _json_yang_link(uid, source, destination, extra): | ||||
|     link = { | ||||
|         'link-id': uid, | ||||
|         'source': { | ||||
|             'source-node': source, | ||||
|         }, | ||||
|         'destination': { | ||||
|             'dest-node': destination, | ||||
|         }, | ||||
|     } | ||||
|     link.update(extra) | ||||
|     return link | ||||
|  | ||||
|  | ||||
| def _store_topology(raw: Dict, equipment, network): | ||||
|     nodes = [] | ||||
|     links = [] | ||||
|  | ||||
|     for n in network.nodes(): | ||||
|         if isinstance(n, elements.Transceiver): | ||||
|             if not hasattr(n, 'type_variety'): | ||||
|                 # raise exceptions.NetworkTopologyError(f"Legacy JSON doesn't specify type_variety for {n!s}") | ||||
|                 # FIXME: Many topologies do not define transponder types. How to solve this? | ||||
|                 n.type_variety = next(iter(equipment['Transceiver'])) | ||||
|             nodes.append({ | ||||
|                 'node-id': n.uid, | ||||
|                 'tip-photonic-topology:transceiver': { | ||||
|                     'model': n.type_variety, | ||||
|                 } | ||||
|             }) | ||||
|             # for x in _next_nodes_except_links(network, n): | ||||
|             #     links.append(_json_yang_link(f'{n.uid} - {x.uid}', n.uid, x.uid, {}) | ||||
|         elif isinstance(n, elements.Edfa): | ||||
|             amp_data = { | ||||
|                 'model': n.type_variety, | ||||
|             } | ||||
|             if n.operational.gain_target is not None: | ||||
|                 amp_data['gain-target'] = str(n.operational.gain_target) | ||||
|             if n.operational.delta_p is not None: | ||||
|                 amp_data['delta-p'] = str(n.operational.delta_p) | ||||
|             if n.operational.tilt_target is not None: | ||||
|                 amp_data['tilt-target'] = str(n.operational.tilt_target) | ||||
|             if n.operational.out_voa is not None: | ||||
|                 amp_data['out-voa-target'] = str(n.operational.out_voa) | ||||
|             nodes.append({ | ||||
|                 'node-id': n.uid, | ||||
|                 'tip-photonic-topology:amplifier': amp_data, | ||||
|             }) | ||||
|         elif isinstance(n, elements.Roadm): | ||||
|             if not hasattr(n, 'type_variety'): | ||||
|                 raise exceptions.NetworkTopologyError(f"Legacy JSON doesn't specify type_variety for {n!s}") | ||||
|             nodes.append({ | ||||
|                 'node-id': n.uid, | ||||
|                 'tip-photonic-topology:roadm': { | ||||
|                     'model': n.type_variety, | ||||
|                     'target-egress-per-channel-power': str(n.params.target_pch_out_db), | ||||
|                     # FIXME: more | ||||
|                 } | ||||
|             }) | ||||
|         elif isinstance(n, elements.Fused): | ||||
|             nodes.append({ | ||||
|                 'node-id': n.uid, | ||||
|                 'tip-photonic-topology:attenuator': { | ||||
|                     'attenuation': str(n.loss), | ||||
|                 } | ||||
|             }) | ||||
|         elif isinstance(n, elements.Fiber): | ||||
|             ingress_node = next(network.predecessors(n)) | ||||
|             egress_node = next(network.successors(n)) | ||||
|             specific = { | ||||
|                 'tip-photonic-topology:fiber': { | ||||
|                     'type': n.type_variety, | ||||
|                     'length': str(n.params.length * 1e-3), | ||||
|                     'attenuation-in': str(n.params.att_in), | ||||
|                     'conn-att-in': str(n.params.con_in), | ||||
|                     'conn-att-out': str(n.params.con_out), | ||||
|                     # FIXME: more? | ||||
|                 } | ||||
|             } | ||||
|             links.append(_json_yang_link(n.uid, ingress_node.uid, egress_node.uid, specific)) | ||||
|         else: | ||||
|             raise NotImplementedError(f'Internal error: unhandled node {n!s}') | ||||
|  | ||||
|     for edge in network.edges(): | ||||
|         if isinstance(edge[0], elements.Fiber): | ||||
|             if isinstance(edge[1], elements.Fiber): | ||||
|                 raise exceptions.NetworkTopologyError(f"Fiber connected to a Fiber: {edge[0].uid}, {edge[1].uid}") | ||||
|             else: | ||||
|                 # nt:link got created when the Fiber node was processed | ||||
|                 continue | ||||
|         elif isinstance(edge[1], elements.Fiber): | ||||
|             # nt:link got created when the Fiber node was processed | ||||
|             continue | ||||
|         link = {'tip-photonic-topology:patch': {}} | ||||
|         if isinstance(edge[0], elements.Roadm): | ||||
|             per_degree_powers = edge[0].params.per_degree_pch_out_db | ||||
|             next_node_name = edge[1].uid | ||||
|             link['tip-photonic-topology:patch']['roadm-target-egress-per-channel-power'] = str( | ||||
|                 per_degree_powers.get(next_node_name, edge[0].params.target_pch_out_db)) | ||||
|         links.append(_json_yang_link(f'patch{{{edge[0].uid}, {edge[1].uid}}}', edge[0].uid, edge[1].uid, link)) | ||||
|  | ||||
|     raw['ietf-network:networks'] = { | ||||
|         'network': [{ | ||||
|             'network-id': 'GNPy', | ||||
|             'network-types': { | ||||
|                 'tip-photonic-topology:photonic-topology': {}, | ||||
|             }, | ||||
|             'node': nodes, | ||||
|             'ietf-network-topology:link': links, | ||||
|         }],  | ||||
|     } | ||||
|  | ||||
|  | ||||
| def save_to_json(equipment: Dict[str, Dict[str, Any]], network) -> Dict: | ||||
|     '''Save the in-memory equipment library into a dict with YANG-formatted data''' | ||||
|     dm = create_datamodel() | ||||
|  | ||||
|     for k in ('Edfa', 'Fiber', 'Span', 'SI', 'Transceiver', 'Roadm'): | ||||
|         if k not in equipment: | ||||
|             raise exceptions.ConfigurationError(f'No "{k}" in the equipment library') | ||||
|     for k in ('Span', 'SI'): | ||||
|         if 'default' not in equipment[k]: | ||||
|             raise exceptions.ConfigurationError('No ["{k}"]["default"] in the equipment library') | ||||
|  | ||||
|     # FIXME: what do we do with these amps? Is this detection a good thing, btw? | ||||
|     # legacy_raman = [name for (name, amp) in equipment['Edfa'].items() if amp.raman] | ||||
|     # if legacy_raman: | ||||
|     #     raise exceptions.ConfigurationError( | ||||
|     #         f'Legacy Raman amplifiers are not supported, remove them from configuration: {legacy_raman}') | ||||
|  | ||||
|     span: _ji.Span = equipment['Span']['default'] | ||||
|     spectrum: _ji.SI = equipment['SI']['default'] | ||||
|  | ||||
|     raw = { | ||||
|         "tip-photonic-equipment:amplifier": [_store_equipment_edfa(k, v) for (k, v) in equipment['Edfa'].items()], | ||||
|         "tip-photonic-equipment:fiber": | ||||
|             [_store_equipment_fiber(k, v) for (k, v) in equipment['Fiber'].items() if k not in equipment.get('RamanFiber', {})] + | ||||
|             [_store_equipment_fiber(k, v) for (k, v) in equipment.get('RamanFiber', {}).items()], | ||||
|         "tip-photonic-equipment:transceiver": [_store_equipment_transceiver(k, v) for (k, v) in equipment['Transceiver'].items()], | ||||
|         "tip-photonic-equipment:roadm": [_store_equipment_roadm(k, v) for (k, v) in equipment['Roadm'].items()], | ||||
|         "tip-photonic-simulation:simulation": { | ||||
|             'grid': { | ||||
|                 'frequency-min': str(spectrum.f_min / _conv.THZ), | ||||
|                 'frequency-max': str(spectrum.f_max / _conv.THZ), | ||||
|                 'spacing': str(spectrum.spacing / _conv.GIGA), | ||||
|                 'power': str(spectrum.power_dbm), | ||||
|                 'tx-roll-off': str(spectrum.roll_off), | ||||
|                 'tx-osnr': str(spectrum.tx_osnr), | ||||
|                 'baud-rate': str(spectrum.baud_rate / _conv.GIGA), | ||||
|             }, | ||||
|             'autodesign': { | ||||
|                 'allowed-inline-edfa': [k for (k, v) in equipment['Edfa'].items() if v.allowed_for_design], | ||||
|                 'power-adjustment-for-span-loss': { | ||||
|                     'maximal-reduction': str(span.delta_power_range_db[0]), | ||||
|                     'maximal-boost': str(span.delta_power_range_db[1]), | ||||
|                     'excursion-step-size': str(span.delta_power_range_db[2]), | ||||
|                 }, | ||||
|             }, | ||||
|             'system-margin': str(spectrum.sys_margins), | ||||
|         }, | ||||
|     } | ||||
|     if span.power_mode: | ||||
|         raw['tip-photonic-simulation:simulation']['autodesign']['power-mode'] = { | ||||
|             'power-sweep': { | ||||
|                 'start': str(spectrum.power_range_db[0]), | ||||
|                 'stop': str(spectrum.power_range_db[1]), | ||||
|                 'step-size': str(spectrum.power_range_db[2]), | ||||
|             }, | ||||
|         } | ||||
|     else: | ||||
|         raw['tip-photonic-simulation:simulation']['autodesign']['gain-mode'] = [None] | ||||
|  | ||||
|     if network is not None: | ||||
|         _store_topology(raw, equipment, network) | ||||
|  | ||||
|     data = dm.from_raw(raw) | ||||
|     data.validate() | ||||
|     return data.raw_value() | ||||
							
								
								
									
										104
									
								
								gnpy/yang/tip/tip-onos-topology.yang
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								gnpy/yang/tip/tip-onos-topology.yang
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | ||||
| module tip-onos-topology { | ||||
|   yang-version 1.1; | ||||
|   namespace "https://oopt.telecominfraproject.com/yang/onos-topology"; | ||||
|   prefix "gnpy-onos"; | ||||
|  | ||||
|   import ietf-network { | ||||
|     prefix nw; | ||||
|     revision-date 2018-02-26; | ||||
|   } | ||||
|  | ||||
|   import ietf-network-topology { | ||||
|     prefix nt; | ||||
|     revision-date 2018-02-26; | ||||
|   } | ||||
|  | ||||
|   organization "Telecom Infrastructure Project"; | ||||
|   contact "https://github.com/Telecominfraproject/oopt-gnpy"; | ||||
|   description "Feeding GNPy simulations into ONOS | ||||
|  | ||||
|   GNPy and ONOS have different understanding of what \"a node\" is. | ||||
|   In GNPy, a typical ROADM, and even a ROADM degree, comprises one GNPy-level element for the \"switching matrix\", and also an extra element for each integrated EDFA. | ||||
|   This presents certain issues when mapping ONOS-level requests with GNPy-level path information -- simply because the node IDs *cannot* be the same. | ||||
|  | ||||
|   No GNPy nodes are \"split\" into several ONOS nodes, but many ONOS elements are split into several GNPy nodes each. | ||||
|  | ||||
|   To use this, just create one nw:network for the GNPy topology, and one nw:network for the ONOS topology. | ||||
|   The node IDs in GNPy are arbitrary, while the node IDs in this topology must map directly to ONOS Device URLs. | ||||
|   Create a pair of nt:network/node/supporting-node/node-ref and network-ref going from the ONOS topology to the GNPy topology. | ||||
|   GNPy's experimental API server will use this information to automatically build a device configuration JSON that can be fed to ONOS. | ||||
|  | ||||
|   The links are a bit more complex, because GNPy does not use ports -- just devices. | ||||
|   All connections in GNPy are unidirectional, whereas in ONOS we only use bidirectional ones -- but with a given port. | ||||
|   This is required so that, e.g., ONOS knows how to determine the egress port number when routing a MC in a ROADM. | ||||
|   "; | ||||
|  | ||||
|   revision 2021-06-06 { | ||||
|     description "Initial release"; | ||||
|     reference "Internal documentation"; | ||||
|   } | ||||
|  | ||||
|   augment "/nw:networks/nw:network/nw:network-types" { | ||||
|     description "ONOS topology for use with GNPy"; | ||||
|     container onos-topology { | ||||
|       presence "Devices for ONOS"; | ||||
|       description "Devices for ONOS"; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   augment "/nw:networks/nw:network/nw:node" { | ||||
|     when "../nw:network-types/gnpy-onos:onos-topology"; | ||||
|     description "ONOS devices"; | ||||
|  | ||||
|     container device { | ||||
|       description "A device that ONOS can connect to | ||||
|  | ||||
|       Use nt:node/supporting-node to tie this with GNPy-level nodes. | ||||
|       "; | ||||
|       leaf name { | ||||
|         type string; | ||||
|         mandatory true; | ||||
|         description "A free-form title"; | ||||
|       } | ||||
|       leaf grid-x { | ||||
|         type int16; | ||||
|         mandatory true; | ||||
|         description "Position in ONOS' topology view (X)"; | ||||
|       } | ||||
|       leaf grid-y { | ||||
|         type int16; | ||||
|         mandatory true; | ||||
|         description "Position in ONOS' topology view (Y)"; | ||||
|       } | ||||
|       leaf driver { | ||||
|         type string; | ||||
|         mandatory true; | ||||
|         description "Driver ID"; | ||||
|       } | ||||
|       container netconf { | ||||
|         description "Protocol options for NETCONF connections"; | ||||
|         leaf username { | ||||
|           type string; | ||||
|           mandatory true; | ||||
|           description "Login name"; | ||||
|         } | ||||
|         leaf password { | ||||
|           type string; | ||||
|           mandatory true; | ||||
|           description "Password in cleartext"; | ||||
|         } | ||||
|         leaf idle-timeout { | ||||
|           type uint16; | ||||
|           description "Just use 0 here"; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   augment "/nw:networks/nw:network/nt:link" { | ||||
|     when "../nw:network-types/gnpy-onos:onos-topology"; | ||||
|     description "ONOS device links"; | ||||
|  | ||||
|     // right now we just don't to anything; we create a link with a magic name, and that's all | ||||
|   } | ||||
| } | ||||
							
								
								
									
										694
									
								
								gnpy/yang/tip/tip-photonic-equipment.yang
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										694
									
								
								gnpy/yang/tip/tip-photonic-equipment.yang
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,694 @@ | ||||
| module tip-photonic-equipment { | ||||
|   yang-version 1.1; | ||||
|   namespace "https://oopt.telecominfraproject.com/yang/equipment"; | ||||
|   prefix "tip-pe"; | ||||
|  | ||||
|   organization "Telecom Infrastructure Project"; | ||||
|   contact "https://github.com/Telecominfraproject/oopt-gnpy"; | ||||
|   description "Catalog of photonic equipment for simulating signal propagation via the OOPT-PSE GNPy tool"; | ||||
|  | ||||
|   revision 2020-09-01 { | ||||
|     description "Initial release"; | ||||
|     reference "Internal documentation"; | ||||
|   } | ||||
|  | ||||
|   typedef db-ratio { | ||||
|     type decimal64 { | ||||
|       fraction-digits 2; | ||||
|     } | ||||
|     units "dB"; | ||||
|     description "Decibels"; | ||||
|   } | ||||
|  | ||||
|   typedef noise-figure { | ||||
|     type db-ratio { | ||||
|       range "3.0 .. 20.0"; | ||||
|     } | ||||
|     description "Noise Figure of an amplifier"; | ||||
|   } | ||||
|  | ||||
|   typedef gain { | ||||
|     type db-ratio { | ||||
|       range "0 .. 40.0"; | ||||
|     } | ||||
|     description "Gain of an amplifier"; | ||||
|   } | ||||
|  | ||||
|   typedef power { | ||||
|     type decimal64 { | ||||
|       fraction-digits 2; | ||||
|       range "-99.9 .. 30.0"; | ||||
|     } | ||||
|     units "dBm"; | ||||
|     description "Optical power in dBm"; | ||||
|   } | ||||
|  | ||||
|   typedef carrier-frequency { | ||||
|     type decimal64 { | ||||
|       fraction-digits 7; | ||||
|       range "191.0 .. 197.0"; | ||||
|     } | ||||
|     units "THz"; | ||||
|     description "Optical frequency of a signal"; | ||||
|   } | ||||
|  | ||||
|   typedef frequency-channel-spacing { | ||||
|     type decimal64 { | ||||
|       fraction-digits 7; | ||||
|       range "6.25 .. 200.0"; | ||||
|     } | ||||
|     units "GHz"; | ||||
|     description "Channel spacing"; | ||||
|   } | ||||
|  | ||||
|   typedef frequency-raman-pump { | ||||
|     type decimal64 { | ||||
|       fraction-digits 3; | ||||
|       range "196.0 .. 260.0"; | ||||
|     } | ||||
|     units "THz"; | ||||
|     description "Optical frequency of a Raman pumping laser"; | ||||
|   } | ||||
|  | ||||
|   typedef baud-rate { | ||||
|     type decimal64 { | ||||
|       fraction-digits 4; | ||||
|       range "10 .. 130"; | ||||
|     } | ||||
|     units "Gbaud"; | ||||
|     description "Symbol rate"; | ||||
|   } | ||||
|  | ||||
|   typedef roll-off { | ||||
|     type decimal64 { | ||||
|       fraction-digits 4; | ||||
|       range "0 .. 1"; | ||||
|     } | ||||
|     description "Roll-off parameter (β) of the TX pulse shaping filter. This assumes a raised-cosine filter."; | ||||
|   } | ||||
|  | ||||
|   typedef cd { | ||||
|     type decimal64 { | ||||
|       fraction-digits 2; | ||||
|       range "-50000 .. 50000"; | ||||
|     } | ||||
|     units "ps × nm⁻¹"; | ||||
|     description "Chromatic Dispersion (CD)."; | ||||
|   } | ||||
|  | ||||
|   typedef pmd { | ||||
|     type decimal64 { | ||||
|       fraction-digits 4; | ||||
|     } | ||||
|     units "ps"; | ||||
|     description "Polarization mode dispersion (PMD)."; | ||||
|   } | ||||
|  | ||||
|   typedef polynomial-coefficient { | ||||
|     type decimal64 { | ||||
|       fraction-digits 12; | ||||
|     } | ||||
|     description "One coefficient within a polynomial"; | ||||
|   } | ||||
|  | ||||
|   grouping cubic-polynomial-coefficients { | ||||
|     description "Coefficients for a polynomial of a third degree: f(x) = a*x³ + b*x² + c*x + d"; | ||||
|     leaf a { | ||||
|       type polynomial-coefficient; | ||||
|       mandatory true; | ||||
|       description "Cubic (x³) coefficient"; | ||||
|     } | ||||
|     leaf b { | ||||
|       type polynomial-coefficient; | ||||
|       mandatory true; | ||||
|       description "Quadratic (x²) coefficient"; | ||||
|     } | ||||
|     leaf c { | ||||
|       type polynomial-coefficient; | ||||
|       mandatory true; | ||||
|       description "Linear (x) coefficient"; | ||||
|     } | ||||
|     leaf d { | ||||
|       type polynomial-coefficient; | ||||
|       mandatory true; | ||||
|       description "Offset (+) coefficient"; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   grouping amp-spectrum-profile { | ||||
|     description "Changes in amplifier's operation as a function of frequency"; | ||||
|  | ||||
|     list gain-ripple { | ||||
|       leaf frequency { | ||||
|         type carrier-frequency; | ||||
|         description "Frequency for the specific gain ripple deviation"; | ||||
|       } | ||||
|  | ||||
|       leaf gain-ripple { | ||||
|         type db-ratio; | ||||
|         mandatory true; | ||||
|         description "Gain ripple deviation at a given frequency"; | ||||
|       } | ||||
|  | ||||
|       key "frequency"; | ||||
|  | ||||
|       description "Amplifier gain ripple excursion comb list in dB across the frequency range"; | ||||
|     } | ||||
|  | ||||
|     list nf-ripple { | ||||
|       leaf frequency { | ||||
|         type carrier-frequency; | ||||
|         description "Frequency for the specific NF ripple deviation"; | ||||
|       } | ||||
|  | ||||
|       leaf nf-ripple { | ||||
|         type db-ratio; | ||||
|         mandatory true; | ||||
|         description "NF ripple deviation at a given frequency"; | ||||
|       } | ||||
|  | ||||
|       key "frequency"; | ||||
|  | ||||
|       description "Amplifier NF ripple excursion comb list in dB across the frequency range"; | ||||
|     } | ||||
|  | ||||
|     list dynamic-gain-tilt { | ||||
|       leaf frequency { | ||||
|         type carrier-frequency; | ||||
|         description "Frequency for the specific NF ripple deviation"; | ||||
|       } | ||||
|  | ||||
|       leaf dynamic-gain-tilt { | ||||
|         type db-ratio; | ||||
|         mandatory true; | ||||
|         description "DGT at a specified frequency"; | ||||
|       } | ||||
|  | ||||
|       key "frequency"; | ||||
|  | ||||
|       description "Dynamic Gain Tilt (DGT) refers to a relative change of gain | ||||
|       at a given frequency when compared to a reference frequency. | ||||
|  | ||||
|       Intro about the model: https://telecominfraproject.workplace.com/groups/OOPT.PSE/permalink/957144244450445/ | ||||
|       Relevant paper: https://www.osapublishing.org/jlt/abstract.cfm?uri=JLT-18-3-343"; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   grouping amp-common { | ||||
|     description "Properties common to all EDFAs except the composite models"; | ||||
|  | ||||
|     leaf frequency-min { | ||||
|       type carrier-frequency; | ||||
|       default 191.325; | ||||
|       description "Minimal frequency supported by this amplifier | ||||
|  | ||||
|       This refers to the edge of the optical spectrum, not to a central frequency of a fixed grid."; | ||||
|     } | ||||
|  | ||||
|     leaf frequency-max { | ||||
|       type carrier-frequency; | ||||
|       default 196.125; | ||||
|       description "Maximal frequency supported by this amplifier | ||||
|  | ||||
|       This refers to the edge of the optical spectrum, not to a central frequency of a fixed grid."; | ||||
|     } | ||||
|  | ||||
|     leaf has-output-voa { | ||||
|       type boolean; | ||||
|       default false; | ||||
|       description "If true, output VOA is present. | ||||
|  | ||||
|       An amplifier with an output VOA can be pushed to operate at its the max-power-out if the output VOA is available."; | ||||
|     } | ||||
|  | ||||
|     leaf gain-flatmax { | ||||
|       type gain; | ||||
|       mandatory true; | ||||
|       description "Maximal gain of the nominal range (without entering the extended range) | ||||
|  | ||||
|       Once the amplifier's gain gets pushed into the extended range, it begins to tilt as specified in the dynamic-gain-tilt."; | ||||
|     } | ||||
|  | ||||
|     leaf max-power-out { | ||||
|       type power; | ||||
|       mandatory true; | ||||
|       description "Maximal output power at the amplifier's output port | ||||
|  | ||||
|       The total signal output power will not be allowed beyond this value."; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
|   list amplifier { | ||||
|     key "type"; | ||||
|     description "Available amplifier (EDFA) models"; | ||||
|  | ||||
|     leaf type { | ||||
|       type string; | ||||
|       description "Brief identification of the amplifier model. This is used for cross-referencing from topology data."; | ||||
|     } | ||||
|  | ||||
|     uses amp-common { | ||||
|       when "count(composite) = 0"; | ||||
|       description "Common parameters for all EDFAs except aggregated ones"; | ||||
|     } | ||||
|  | ||||
|     leaf gain-min { | ||||
|       type gain; | ||||
|       mandatory true; | ||||
|       description "Minimal possible gain of the amplifier | ||||
|  | ||||
|       If the amplifier's gain is set below this value, the amplifier's input is automatically padded with an attenuator, | ||||
|       and the NF is increased by the attenuation of this padding."; | ||||
|     } | ||||
|  | ||||
|     choice noise-model { | ||||
|       mandatory true; | ||||
|       description "What simulation algorithm to use for this amplifier model"; | ||||
|  | ||||
|       case polynomial-NF { | ||||
|         container polynomial-NF { | ||||
|           description "Whitebox model with detailed information about gain ripple, NF ripple and dynamic gain tilt | ||||
|  | ||||
|           Polynomial coefficients for NF calculation: | ||||
|  | ||||
|             f(x) = a*x³ + b*x² + c*x + d | ||||
|  | ||||
|             NF = f(gain_max - gain) | ||||
|  | ||||
|           This model can be also used for fixed-gain fixed-NF amplifiers. In that case, use: | ||||
|  | ||||
|             a = b = c = 0 | ||||
|             d = NF"; | ||||
|  | ||||
|           uses cubic-polynomial-coefficients; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       case min-max-NF { | ||||
|         container min-max-NF { | ||||
|           description "Operator-focused model | ||||
|  | ||||
|           Performance is defined by the minimal and maximal NF. These are especially suited to model a dual-coil | ||||
|           EDFA with a VOA in between."; | ||||
|  | ||||
|           leaf nf-min { | ||||
|             type noise-figure; | ||||
|             mandatory true; | ||||
|             description "Minimal Noise Figure (operating at the point of the maximal flat gain) | ||||
|  | ||||
|             See gain-flatmax."; | ||||
|           } | ||||
|  | ||||
|           leaf nf-max { | ||||
|             type noise-figure; | ||||
|             mandatory true; | ||||
|             description "Maximal Noise Figure (operating at the minimal gain)"; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       case OpenROADM-ILA { | ||||
|         container OpenROADM-ILA { | ||||
|           description "EDFA model based on the OpenROADM specification for an ILA | ||||
|  | ||||
|           OpenROADM describes amplifier performance in terms of an incremental OSNR as a function of input power: | ||||
|  | ||||
|             Incremental OSNR = a*Pᵢₙ³ + b*Pᵢₙ² + c*Pᵢₙ + d | ||||
|           "; | ||||
|  | ||||
|           uses cubic-polynomial-coefficients; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       case OpenROADM-preamp { | ||||
|         container OpenROADM-preamp { | ||||
|           presence true; | ||||
|           description "Linear impairments of the MW-MW path within an OpenROADM ROADM node | ||||
|  | ||||
|           Unlike GNPy which simulates the preamplifier and the booster separately as two amplifiers for best accuracy, | ||||
|           the OpenROADM specification mandates a certain performance level for a combination of these two amplifiers. | ||||
|           For the express path, the effective noise mask comprises the preamplifier and the booster. | ||||
|           When terminating a channel, the same effective noise mask is mandated for a combination of the preamplifier | ||||
|           and the drop stage. | ||||
|  | ||||
|           This NF model provides all of the linear impairments to the signal, including those which are incurred by | ||||
|           the booster in a real network."; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       case OpenROADM-booster { | ||||
|         container OpenROADM-booster { | ||||
|           presence true; | ||||
|           description "A faux, \"zero-noise\" amplifier for use along with OpenROADM-preamp as a booster."; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       case composite { | ||||
|         container composite { | ||||
|           description "Dual-stage amplifier combines two distinct amplifiers | ||||
|  | ||||
|           The first amplifier will be always operated at its maximal gain (and therefore its best NF)."; | ||||
|  | ||||
|           leaf preamp { | ||||
|             type leafref { | ||||
|               path "/tip-pe:amplifier/type"; | ||||
|             } | ||||
|             must "count(deref(.)/../composite) = 0" { | ||||
|               error-message "First (preamp) stage of a composite amplifier cannot be a composite amplifier"; | ||||
|             } | ||||
|             must "../../gain-min >= deref(.)/../gain-min" { | ||||
|               error-message "Minimal total gain of a composite EDFA cannot be lower that the minimal gain of the preamp"; | ||||
|             } | ||||
|             mandatory true; | ||||
|             description "Amplifier type used as a preamplifier, i.e., the first stage"; | ||||
|           } | ||||
|  | ||||
|           leaf booster { | ||||
|             type leafref { | ||||
|               path "/tip-pe:amplifier/type"; | ||||
|             } | ||||
|             must "count(deref(.)/../composite) = 0" { | ||||
|               error-message "Second (booster) stage of a composite amplifier cannot be a composite amplifier"; | ||||
|             } | ||||
|             must "(deref(.)/../frequency-min <= deref(../preamp)/../frequency-max) and | ||||
|                   (deref(.)/../frequency-max >= deref(../preamp)/../frequency-min)" { | ||||
|               error-message "booster/preamp operating frequencies do not overlap"; | ||||
|             } | ||||
|             mandatory true; | ||||
|             description "Amplifier type used as a booster, i.e., the second stage"; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       case raman-approximation { | ||||
|         container raman-approximation { | ||||
|           description "Emulate a Raman amplifier with a possibly negative NF | ||||
|  | ||||
|           This NF model assumes a particular, fixed NF. It is similar to the polynomial-NF model, except that the | ||||
|           effective NF value is fixed (and therefore it does not vary with the amplifier's operating point), and | ||||
|           that the effective NF can be described as a negative value. | ||||
|  | ||||
|           Use this for model to (roughly) emulate a Raman amplifier when the detailed description of Raman pumps | ||||
|           is not available from the equipment vendor."; | ||||
|  | ||||
|           leaf nf { | ||||
|             type db-ratio { | ||||
|               range "-5.0 .. 20.0"; | ||||
|             } | ||||
|             mandatory true; | ||||
|             description "Noise Figure (NF) of the amplifier"; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     uses amp-spectrum-profile { | ||||
|       when "count(deref(.)/composite) = 0"; | ||||
|     } | ||||
|  | ||||
|     leaf model-precision { | ||||
|       type enumeration { | ||||
|         enum public-approximation { | ||||
|           description "These data come from a publicly available datasheet, and as such might be only an approximate representation"; | ||||
|         } | ||||
|         enum reasonably-precise { | ||||
|           description "The GNPy team believes that these are reasonably accurate"; | ||||
|         } | ||||
|       } | ||||
|       default reasonably-precise; | ||||
|       description "How precise are the modeling data | ||||
|  | ||||
|       If a simulation runs with only approximate inputs, the simulation results might be \"tainted\" with inaccuracies."; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
|   list fiber { | ||||
|     key "type"; | ||||
|     description "Available fiber types"; | ||||
|  | ||||
|     leaf type { | ||||
|       type string; | ||||
|       description "Unique identification of the fiber type. This is used for cross-referencing from topology data."; | ||||
|     } | ||||
|  | ||||
|     leaf chromatic-dispersion { | ||||
|       type decimal64 { | ||||
|         fraction-digits 6; | ||||
|         range "-25 .. 25"; | ||||
|       } | ||||
|       units "ps × nm⁻¹ × km⁻¹"; | ||||
|       mandatory true; | ||||
|       description "Chromatic dispersion"; | ||||
|     } | ||||
|  | ||||
|     leaf chromatic-dispersion-slope { | ||||
|       type decimal64 { | ||||
|         fraction-digits 8; | ||||
|         range "0 .. 0.1"; | ||||
|       } | ||||
|       units "ps × nm⁻² × km⁻¹"; | ||||
|       default "0.07"; | ||||
|       description "Chromatic dispersion slope is related to the β₃ coefficient | ||||
|  | ||||
|       Cf. Abramczyk, Halina. Dispersion phenomena in optical fibers. Virtual European University on Lasers, 2005. | ||||
|  | ||||
|       http://mitr.p.lodz.pl/evu/lectures/Abramczyk3.pdf"; | ||||
|     } | ||||
|  | ||||
|     leaf gamma { | ||||
|       type decimal64 { | ||||
|         fraction-digits 8; | ||||
|         range "0.5 .. 2.5"; | ||||
|       } | ||||
|       units "W⁻¹ × km⁻¹"; | ||||
|       mandatory true; | ||||
|       description "Fiber's γ coefficient | ||||
|  | ||||
|       See, e.g., A. Carena, G. Bosco, V. Curri, P. Poggiolini, M. Tapia Taiba, and F. Forghieri. Statistical characterization | ||||
|       of PM-QPSK signals after propagation in uncompensated fiber links. In European Conference on Optical Communications, | ||||
|       2010, 1–3. IEEE, 2010-09. | ||||
|  | ||||
|       URL: http://ieeexplore.ieee.org/document/5621509/ | ||||
|       doi:10.1109/ECOC.2010.5621509"; | ||||
|     } | ||||
|  | ||||
|     leaf pmd-coefficient { | ||||
|       type decimal64 { | ||||
|         fraction-digits 10; | ||||
|         range "0 .. 10"; | ||||
|       } | ||||
|       units "ps × √(km)⁻¹"; | ||||
|       mandatory true; | ||||
|       description "Polarization mode dispersion (PMD) coefficient"; | ||||
|     } | ||||
|  | ||||
|     list raman-efficiency { | ||||
|         key "delta-frequency"; | ||||
|         description "Efficiency of Raman amplification in the fiber medium per operating frequency | ||||
|  | ||||
|         See, e.g., J. Bromage. Raman Amplification for Fiber Communications Systems. In J. Lightwave Technol.  22, 79- (2004)."; | ||||
|  | ||||
|         leaf delta-frequency { | ||||
|           type decimal64 { | ||||
|             fraction-digits 3; | ||||
|             range "0 .. 60"; | ||||
|           } | ||||
|           units "THz"; | ||||
|           description "Spectral difference between the pumping photon and the one receiving energy"; | ||||
|         } | ||||
|  | ||||
|         leaf cr { | ||||
|           type decimal64 { | ||||
|             fraction-digits 12; | ||||
|             range "0 .. 1"; | ||||
|           } | ||||
|           mandatory true; | ||||
|           description "Normalized Raman efficiency (the Cᵣ parameter)"; | ||||
|         } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
|   list transceiver { | ||||
|     key "type"; | ||||
|     description "Available transceivers"; | ||||
|  | ||||
|     leaf type { | ||||
|       type string; | ||||
|       description "Unique identification of the transceiver type. This is used for cross-referencing from topology data."; | ||||
|     } | ||||
|  | ||||
|     leaf frequency-min { | ||||
|       type carrier-frequency; | ||||
|       default 191.35; | ||||
|       description "Minimal frequency supported by this transceiver model"; | ||||
|     } | ||||
|  | ||||
|     leaf frequency-max { | ||||
|       type carrier-frequency; | ||||
|       default 196.1; | ||||
|       description "Maximal frequency supported by this transceiver model"; | ||||
|     } | ||||
|  | ||||
|     list mode { | ||||
|       key "name"; | ||||
|       min-elements 1; | ||||
|       description "Operating mode of a transceiver"; | ||||
|  | ||||
|       leaf name { | ||||
|         type string; | ||||
|         description "Name of this operating mode"; | ||||
|       } | ||||
|  | ||||
|       leaf bit-rate { | ||||
|         type uint16 { | ||||
|           range "100 .. 1000"; | ||||
|         } | ||||
|         units "Gbits * s⁻¹"; | ||||
|         description "Data bit rate"; | ||||
|       } | ||||
|  | ||||
|       leaf baud-rate { | ||||
|         type baud-rate; | ||||
|         mandatory true; | ||||
|         description "Symbol baud rate"; | ||||
|       } | ||||
|  | ||||
|       leaf required-osnr { | ||||
|         type db-ratio { | ||||
|           range "10..40"; | ||||
|         } | ||||
|         mandatory true; | ||||
|         description "Minimal required OSNR at the Rx port per 0.1nm of bandwidth"; | ||||
|       } | ||||
|  | ||||
|       leaf in-band-tx-osnr { | ||||
|         type db-ratio; | ||||
|         mandatory true; | ||||
|         description "Worst-case guaranteed initial OSNR at the Tx port per 0.1nm of bandwidth | ||||
|  | ||||
|         Only the in-band OSNR is considered."; | ||||
|       } | ||||
|  | ||||
|       leaf grid-spacing { | ||||
|         type frequency-channel-spacing; | ||||
|         mandatory true; | ||||
|         description "Minimal grid spacing | ||||
|  | ||||
|         This includes the effective channel spectral bandwidth as well as any operational constraints and policies."; | ||||
|       } | ||||
|  | ||||
|       leaf tx-roll-off { | ||||
|         type roll-off; | ||||
|         mandatory true; | ||||
|         description "Roll-off parameter (β) of the TX pulse shaping filter. This assumes a raised-cosine filter."; | ||||
|       } | ||||
|  | ||||
|       leaf max-chromatic-dispersion { | ||||
|         type cd; | ||||
|         description "Maximal allowed CD (a hard limit)"; | ||||
|       } | ||||
|  | ||||
|       leaf max-polarization-mode-dispersion { | ||||
|         type pmd { | ||||
|           range "0 .. 500"; | ||||
|         } | ||||
|         description "Maximal allowed PMD (a hard limit)"; | ||||
|       } | ||||
|  | ||||
|       list chromatic-and-polarization-dispersion-penalty { | ||||
|         key "chromatic-dispersion  polarization-mode-dispersion"; | ||||
|  | ||||
|         leaf chromatic-dispersion { | ||||
|           type cd; | ||||
|           must ". <= ../../max-chromatic-dispersion" { | ||||
|             error-message "CD in the penalty matrix exceeds receiver tolerance"; | ||||
|           } | ||||
|           description "CD for a given penalty"; | ||||
|         } | ||||
|  | ||||
|         leaf polarization-mode-dispersion { | ||||
|           type pmd { | ||||
|             range "0 .. 500"; | ||||
|           } | ||||
|           must ". <= ../../max-polarization-mode-dispersion" { | ||||
|             error-message "PMD in the penalty matrix exceeds receiver tolerance"; | ||||
|           } | ||||
|           description "PMD for a given penalty"; | ||||
|         } | ||||
|  | ||||
|         leaf penalty { | ||||
|           type db-ratio { | ||||
|             range "-5 .. 10"; | ||||
|           } | ||||
|           mandatory true; | ||||
|           description "Resulting GSNR penalty at the specified CD and PMD"; | ||||
|         } | ||||
|  | ||||
|         description "GSNR penalty for a combination of a CD and PMD | ||||
|  | ||||
|         The receiver performance should be de-rated by a given `penalty` for a specified combination of CD and PMD. | ||||
|         GNPy will use linear approximation between the provided datapoints in the CD/PMD matrix. | ||||
|         "; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
|   list roadm { | ||||
|     key "type"; | ||||
|     description "ROADM - Reconfigurable Optical Add/Drop Multiplexer"; | ||||
|  | ||||
|     leaf type { | ||||
|       type string; | ||||
|       description "Unique identification of the transponder type. This is used for cross-referencing from topology data."; | ||||
|     } | ||||
|  | ||||
|     leaf add-drop-osnr { | ||||
|       type db-ratio; | ||||
|       mandatory true; | ||||
|       description "OSNR penalty introduced by the Add stage and the Drop stage of this ROADM model | ||||
|  | ||||
|       Effective degradation of the signal, taking into account both the Add and the Drop stages of this ROADM model."; | ||||
|     } | ||||
|  | ||||
|     leaf target-channel-out-power { | ||||
|       type power; | ||||
|       mandatory true; | ||||
|       description "Per-channel target TX power towards the egress amplifier | ||||
|  | ||||
|       Within GNPy, a ROADM is expected to attenuate any signal that enters the ROADM node to this level. This can be | ||||
|       overridden on a per-link basis in the network topology."; | ||||
|     } | ||||
|  | ||||
|     leaf polarization-mode-dispersion { | ||||
|       type pmd { | ||||
|         range "0 .. 5"; | ||||
|       } | ||||
|       mandatory true; | ||||
|       description "Polarization mode dispersion (PMD) penalty of the express path within this ROADM model"; | ||||
|     } | ||||
|  | ||||
|     leaf-list compatible-preamp { | ||||
|       type leafref { | ||||
|         path "/tip-pe:amplifier/type"; | ||||
|       } | ||||
|       description "A set of allowed amplifier types to be used in the ingress direction | ||||
|  | ||||
|       If empty, autodesign is allowed to pick any amplifier as a preamp."; | ||||
|     } | ||||
|  | ||||
|     leaf-list compatible-booster { | ||||
|       type leafref { | ||||
|         path "/tip-pe:amplifier/type"; | ||||
|       } | ||||
|       description "A set of allowed amplifier types to be used in the egress direction | ||||
|  | ||||
|       If empty, autodesign is allowed to pick any amplifier as a booster."; | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
| } | ||||
							
								
								
									
										244
									
								
								gnpy/yang/tip/tip-photonic-simulation.yang
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								gnpy/yang/tip/tip-photonic-simulation.yang
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,244 @@ | ||||
| module tip-photonic-simulation { | ||||
|   yang-version 1.1; | ||||
|   namespace "https://oopt.telecominfraproject.com/yang/simulation"; | ||||
|   prefix "tip-sim"; | ||||
|  | ||||
|   import tip-photonic-equipment { | ||||
|     prefix tip-pe; | ||||
|     revision-date 2020-09-01; | ||||
|   } | ||||
|  | ||||
|   organization "Telecom Infrastructure Project"; | ||||
|   contact "https://github.com/Telecominfraproject/oopt-gnpy"; | ||||
|   description "Simulation settings for GNPy"; | ||||
|  | ||||
|   revision 2020-09-01 { | ||||
|     description "Initial release"; | ||||
|     reference "Internal documentation"; | ||||
|   } | ||||
|  | ||||
|   container simulation { | ||||
|     presence "Activates a GNPy simulation"; | ||||
|     description "Simulation settings and per-run input parameters"; | ||||
|  | ||||
|     choice spectrum { | ||||
|       mandatory true; | ||||
|       description "Spectral load for planning considerations | ||||
|  | ||||
|       This is the channel allocation for which GNPy will optimize. It should represent the end-of-life spectrum allocation."; | ||||
|  | ||||
|       case grid { | ||||
|         container grid { | ||||
|           description "Homogeneous channel allocation on a fixed grid"; | ||||
|  | ||||
|           leaf frequency-min { | ||||
|             type tip-pe:carrier-frequency; | ||||
|             default 191.35; | ||||
|             description "Central frequency of the first (lowest) channel"; | ||||
|           } | ||||
|  | ||||
|           leaf frequency-max { | ||||
|             type tip-pe:carrier-frequency; | ||||
|             default 196.1; | ||||
|             description "Central frequency of the last (highest) channel"; | ||||
|           } | ||||
|  | ||||
|           leaf spacing { | ||||
|             type tip-pe:frequency-channel-spacing; | ||||
|             default 50.0; | ||||
|             description "Grid spacing"; | ||||
|           } | ||||
|  | ||||
|           leaf baud-rate { | ||||
|             type tip-pe:baud-rate; | ||||
|             mandatory true; | ||||
|             description "Symbol baud rate"; | ||||
|           } | ||||
|  | ||||
|           leaf tx-osnr { | ||||
|             type tip-pe:db-ratio; | ||||
|             mandatory true; | ||||
|             description "Transponder TX signal OSNR"; | ||||
|           } | ||||
|  | ||||
|           leaf tx-roll-off { | ||||
|             type tip-pe:roll-off; | ||||
|             mandatory true; | ||||
|             description "Roll-off parameter (β) of the TX pulse shaping filter. This assumes a raised-cosine filter."; | ||||
|           } | ||||
|  | ||||
|           leaf power { | ||||
|             type tip-pe:power; | ||||
|             mandatory true; | ||||
|             description ""; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     container autodesign { | ||||
|       description "Optimization parameters"; | ||||
|  | ||||
|       choice edfa-gain-strategy { | ||||
|         mandatory true; | ||||
|         description "Optimization strategy for setting amplifier operating mode"; | ||||
|  | ||||
|         case power-mode { | ||||
|           container power-mode { | ||||
|             description "Whatever GNPy power-mode actually means"; // FIXME | ||||
|  | ||||
|             container power-sweep { | ||||
|               presence "Vary the initial launch power"; | ||||
|               description "Varying the initial launch power"; | ||||
|  | ||||
|               leaf start { | ||||
|                 type tip-pe:db-ratio; | ||||
|                 mandatory true; | ||||
|                 description "Initial delta from the reference power when determining the best initial launch power"; | ||||
|               } | ||||
|               leaf stop { | ||||
|                 type tip-pe:db-ratio; | ||||
|                 mandatory true; | ||||
|                 description "Final delta from the reference power when determining the best initial launch power"; | ||||
|               } | ||||
|               leaf step-size { | ||||
|                 type tip-pe:db-ratio; | ||||
|                 mandatory true; | ||||
|                 description "Step size when determining the best initial launch power"; | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         case gain-mode { | ||||
|           leaf gain-mode { | ||||
|             type empty; | ||||
|             mandatory true; | ||||
|             // FIXME: describe this | ||||
|             description "Set EDFA gain based on previous span loss | ||||
|  | ||||
|             For all EDFAs whose gain has not been set manually, set the gain based on the following rules: | ||||
|  | ||||
|             1) Set gain to the preceding span loss. | ||||
|  | ||||
|             2) Offset the gains around the reference power (FIXME: what does it mean? | ||||
|  | ||||
|             This will leave the gain of EDFAs which have their gains set manually in the network topology unchanged."; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       container power-adjustment-for-span-loss { | ||||
|         description "Adjusting launch power depending on a span loss | ||||
|  | ||||
|         When in effect, lanuch powers to spans are adjusted based on the total span loss. The span loss is | ||||
|         compared to a reference span of 20dB, and the launch power is adjusted by about 0.3 * loss_difference, | ||||
|         up to a provided maximal adjustment. | ||||
|  | ||||
|         This adjustment is performed for all spans when running in the `power-mode`. When in `gain-mode`, | ||||
|         it affects only EDFAs which do not have an explicitly assigned `delta-p`. | ||||
|         "; | ||||
|  | ||||
|         leaf maximal-reduction { | ||||
|           type tip-pe:power; | ||||
|           mandatory true; | ||||
|           description "Launch power might be reduced by up to this many dB on \"short\" spans"; | ||||
|         } | ||||
|  | ||||
|         leaf maximal-boost { | ||||
|           type tip-pe:power; | ||||
|           mandatory true; | ||||
|           description "Launch power might be increased by up to this many dB on lossy spans"; | ||||
|         } | ||||
|  | ||||
|         leaf excursion-step-size { | ||||
|           type tip-pe:power; | ||||
|           mandatory true; | ||||
|           description "Step size when increasing/decreasing the launch power"; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       leaf-list allowed-inline-edfa { | ||||
|         type leafref { | ||||
|           path "/tip-pe:amplifier/tip-pe:type"; | ||||
|         } | ||||
|         description "Allowed EDFA types to be used as inline amplifiers"; | ||||
|       } | ||||
|  | ||||
|       leaf maximal-span-length { | ||||
|         type uint16 { | ||||
|           range 10..300; | ||||
|         } | ||||
|         units "km"; | ||||
|         default 100; | ||||
|         description "Distance threshold for inserting amplifiers into tentative links | ||||
|  | ||||
|         When working with not-fully-specified fiber links (tentative links), keep the individual fiber below this length. Extra | ||||
|         inline amplifiers will be automatically inserted in between."; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     leaf system-margin { | ||||
|       type tip-pe:power { | ||||
|         range 0..10; | ||||
|       } | ||||
|       default 2; | ||||
|       description "Require this many dBm of GSNR headroom as a safety margin for End-of-Life component deterioration"; | ||||
|     } | ||||
|  | ||||
|     leaf edfa-maximal-extended-power { | ||||
|       type tip-pe:power { | ||||
|         range 0..6; | ||||
|       } | ||||
|       default 2.5; | ||||
|       status deprecated; // FIXME: move this into /tip-photonic-equipment:amplifier once the backend accounts for that argument | ||||
|       description "Allow up to this many dB increase of an amplifier's gain into its extended power range"; | ||||
|     } | ||||
|  | ||||
|     leaf shortest-span-without-extra-attenuation { | ||||
|       type tip-pe:db-ratio; | ||||
|       default 10.0; | ||||
|       description "When a fiber span has a lower attenuation than this, automatically insert an attenuator at its beginning"; | ||||
|     } | ||||
|  | ||||
|     container nli { | ||||
|       description "Non-linear interference (NLI) simulation options"; | ||||
|  | ||||
|       leaf algorithm { | ||||
|         type enumeration { | ||||
|           enum analytic-gn-model { | ||||
|             description ""; // FIXME: to be filed by Polito | ||||
|           } | ||||
|           enum generalized-gn-spectrally-separated { | ||||
|             description ""; // FIXME: to be filed by Polito | ||||
|           } | ||||
|         } | ||||
|         default generalized-gn-spectrally-separated; | ||||
|         description "What simulation model to use for calculating NLI contribution to the GSNR"; | ||||
|       } | ||||
|  | ||||
|       container raman { | ||||
|         presence "If present, enable Raman-aware simulation"; | ||||
|         description "Global options for Raman-aware simulation"; | ||||
|         // FIXME: this really needs docs! CHeck with Alessio and Andrea et al | ||||
|         // FIXME: space-resolution | ||||
|       } | ||||
|  | ||||
|       // FIXME: grid-size | ||||
|       // FIXME: dispersion-tolerance | ||||
|       // FIXME: phase-shift-tolerance | ||||
|       // FIXME: computed-channels | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   augment "/tip-pe:transceiver/tip-pe:mode" { | ||||
|     description "Transponder mode selection: cost of a particular mode"; | ||||
|  | ||||
|     leaf cost { | ||||
|       type uint32; | ||||
|       units "Arbitrary units"; | ||||
|       default 1; | ||||
|       description "Cost of selecting this mode when determining path feasibility"; | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										330
									
								
								gnpy/yang/tip/tip-photonic-topology.yang
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										330
									
								
								gnpy/yang/tip/tip-photonic-topology.yang
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,330 @@ | ||||
| module tip-photonic-topology { | ||||
|   yang-version 1.1; | ||||
|   namespace "https://oopt.telecominfraproject.com/yang/topology"; | ||||
|   prefix "tip-topo"; | ||||
|  | ||||
|   import tip-photonic-equipment { | ||||
|     prefix tip-pe; | ||||
|     revision-date 2020-09-01; | ||||
|   } | ||||
|  | ||||
|   import tip-photonic-simulation { | ||||
|     prefix tip-sim; | ||||
|     revision-date 2020-09-01; | ||||
|   } | ||||
|  | ||||
|   import ietf-network { | ||||
|     prefix nw; | ||||
|     revision-date 2018-02-26; | ||||
|   } | ||||
|  | ||||
|   import ietf-network-topology { | ||||
|     prefix nt; | ||||
|     revision-date 2018-02-26; | ||||
|   } | ||||
|  | ||||
|   organization "Telecom Infrastructure Project"; | ||||
|   contact "https://github.com/Telecominfraproject/oopt-gnpy"; | ||||
|   description "Network topology for simulating signal propagation via the OOPT-PSE GNPy tool"; | ||||
|  | ||||
|   revision 2020-09-01 { | ||||
|     description "Initial release"; | ||||
|     reference "Internal documentation"; | ||||
|   } | ||||
|  | ||||
|   augment "/nw:networks/nw:network/nw:network-types" { | ||||
|     description "Telecom Infra Project Open Optical Packet Transport Photonic Simulation Environment"; | ||||
|     container photonic-topology { | ||||
|       presence "indicates topology describing optical elements"; | ||||
|       description "The presence of this container indicates a topology with optical elements"; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   grouping link-common-properties { | ||||
|     description "Common fiber parameters which are always known, even when performing autodesign and inserting amplifiers"; | ||||
|  | ||||
|     leaf type { | ||||
|       type leafref { | ||||
|         path "/tip-pe:fiber/tip-pe:type"; | ||||
|       } | ||||
|       mandatory true; | ||||
|       description "Fiber type cross-reference"; | ||||
|     } | ||||
|  | ||||
|     leaf length { | ||||
|       type decimal64 { | ||||
|         fraction-digits 3; | ||||
|       } | ||||
|       units "km"; | ||||
|       mandatory true; | ||||
|       description "Length of the fiber segment"; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   augment "/nw:networks/nw:network/nt:link" { | ||||
|     when "../nw:network-types/tip-topo:photonic-topology"; | ||||
|     description "Connections of optical components"; | ||||
|  | ||||
|     // Unfortunately, ietf-network-topology has `require-instance: false` for source and target nodes, and | ||||
|     // the YANG standard doesn't even allow a deviation to override this. | ||||
|     // Also, a `must` statement is not allowed here, and also not in the `choice` statement. One could do a | ||||
|     // `when` deviation`, but that one produced non-intuitive error messages. | ||||
|     // So we have three copies of that superficial `must`, yay. | ||||
|  | ||||
|     choice link-type { | ||||
|       description "Is this a well-specified fiber, or a link that should be optimized?"; | ||||
|  | ||||
|       case tentative-link { | ||||
|         container tentative-link { | ||||
|           must "count(deref(../nt:source/nt:source-node)) = 1" { | ||||
|             error-message "ietf-network-topology:source/source-node must point to a defined node"; | ||||
|           } | ||||
|           must "count(deref(../nt:destination/nt:dest-node)) = 1" { | ||||
|             error-message "ietf-network-topology:destination/dest-node must point to a defined node"; | ||||
|           } | ||||
|           description "A link where GNPy is expected to inject amplifiers where needed"; | ||||
|           uses link-common-properties; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       case fiber { | ||||
|         container fiber { | ||||
|           must "count(deref(../nt:source/nt:source-node)) = 1" { | ||||
|             error-message "ietf-network-topology:source/source-node must point to a defined node"; | ||||
|           } | ||||
|           must "count(deref(../nt:destination/nt:dest-node)) = 1" { | ||||
|             error-message "ietf-network-topology:destination/dest-node must point to a defined node"; | ||||
|           } | ||||
|  | ||||
|           description "Fiber connection | ||||
|  | ||||
|           This signifies a fiber whose length is already known. No amplifier huts are available, and the fiber will be used as-is."; | ||||
|  | ||||
|           uses link-common-properties; | ||||
|  | ||||
|           leaf loss-per-km { | ||||
|             type decimal64 { | ||||
|               fraction-digits 6; | ||||
|               range "0..10"; | ||||
|             } | ||||
|             units "dB/km"; | ||||
|             default 0.2; | ||||
|             description "Attenuation per kilometer of fiber"; | ||||
|             // FIXME: should we just put total attenuation of that fiber in? | ||||
|           } | ||||
|  | ||||
|           leaf attenuation-in { | ||||
|             type tip-pe:db-ratio { | ||||
|               range "0..100"; | ||||
|             } | ||||
|             default 0; | ||||
|             description "Extra fixed attenuator at the beginning of the fiber"; | ||||
|           } | ||||
|  | ||||
|           leaf conn-att-in { | ||||
|             type tip-pe:db-ratio { | ||||
|               range "0..100"; | ||||
|             } | ||||
|             default 0; | ||||
|             description "Attenuation of the connector at the fiber's beginning"; | ||||
|           } | ||||
|  | ||||
|           leaf conn-att-out { | ||||
|             type tip-pe:db-ratio { | ||||
|               range "0..100"; | ||||
|             } | ||||
|             default 0; | ||||
|             description "Attenuation of the connector at the fiber's end"; | ||||
|           } | ||||
|  | ||||
|           container raman { | ||||
|             must "count(/tip-sim:simulation/tip-sim:nli/tip-sim:raman) = 1" { | ||||
|               error-message "Raman-aware fiber requires a Raman-aware model in global simulation parameters"; | ||||
|             } | ||||
|  | ||||
|             must "count(deref(../type)/../tip-pe:raman-efficiency) > 0" { | ||||
|               error-message "Raman simulation requires specification of fiber's Raman efficiency in the equipment library"; | ||||
|             } | ||||
|  | ||||
|             presence "If present, activate Raman-aware modeling for this fiber"; | ||||
|             description "Raman parameters: SRS awareness and explicit pumping"; | ||||
|  | ||||
|             leaf temperature { | ||||
|               type uint16 { | ||||
|                 range 273..373; | ||||
|               } | ||||
|               units "K"; | ||||
|               mandatory true; | ||||
|               description "Temperature of the fiber"; | ||||
|             } | ||||
|  | ||||
|             list pump { | ||||
|               key "frequency"; | ||||
|  | ||||
|               leaf frequency { | ||||
|                 type tip-pe:frequency-raman-pump; | ||||
|                 mandatory true; | ||||
|                 description "Frequency of this Raman pump laser"; | ||||
|               } | ||||
|  | ||||
|               leaf power { | ||||
|                 type tip-pe:power; | ||||
|                 mandatory true; | ||||
|                 description "Pumping power"; | ||||
|               } | ||||
|  | ||||
|               leaf direction { | ||||
|                 type enumeration { | ||||
|                   enum co-propagating { | ||||
|                     description "Co-propagating Raman pump pumps the power in the same direction as the carried optical signal payload"; | ||||
|                   } | ||||
|                   enum counter-propagating { | ||||
|                     description "Coounter-propagating Raman pump pumps the power in the opposite direction to the carried optical signal payload"; | ||||
|                   } | ||||
|                 } | ||||
|                 mandatory true; | ||||
|                 description "Direction of propagation of this Raman pump"; | ||||
|               } | ||||
|  | ||||
|               description "Raman pump lasers"; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       case patch { | ||||
|         container patch { | ||||
|           must "count(deref(../nt:source/nt:source-node)) = 1" { | ||||
|             error-message "ietf-network-topology:source/source-node must point to a defined node"; | ||||
|           } | ||||
|           must "count(deref(../nt:destination/nt:dest-node)) = 1" { | ||||
|             error-message "ietf-network-topology:destination/dest-node must point to a defined node"; | ||||
|           } | ||||
|  | ||||
|           // FIXME: check booster/preamp restrictions for ROADMs | ||||
|  | ||||
|           description "Direct connection between network elements | ||||
|  | ||||
|           A direct patch cord is a special case of fiber. It is assumed to be very short (a hundred meters at most) so that the | ||||
|           effect of NLI is limited, and that there's negligible attenuation."; | ||||
|  | ||||
|           leaf roadm-target-egress-per-channel-power { | ||||
|             when "count(deref(../../nt:source/nt:source-node)/../roadm) > 0"; | ||||
|             type tip-pe:power; | ||||
|             description "Per-channel tar egress power for signals exiting the ROADM over this link"; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   augment "/nw:networks/nw:network/nw:node" { | ||||
|     when "../nw:network-types/tip-topo:photonic-topology"; | ||||
|     description "Optical elements within a network"; | ||||
|  | ||||
|     choice element { | ||||
|       mandatory true; | ||||
|       description "A physical instance of something"; | ||||
|  | ||||
|       case amplifier-placeholder { | ||||
|         leaf amplifier-placeholder { | ||||
|           type empty; | ||||
|           mandatory true; | ||||
|           description "Intent to place an amplifier, to be replaced by GNPy's autodesign with a specific model"; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       case amplifier { | ||||
|         container amplifier { | ||||
|           description "Amplifier"; | ||||
|  | ||||
|           leaf model { | ||||
|             type leafref { | ||||
|               path "/tip-pe:amplifier/tip-pe:type"; | ||||
|             } | ||||
|             mandatory true; | ||||
|             description "Amplifier model cross-reference"; | ||||
|           } | ||||
|  | ||||
|           leaf gain-target { | ||||
|             type tip-pe:gain; | ||||
|             description "Desired gain of the amplifier | ||||
|  | ||||
|             If not set, GNPy will try to find an optimal operating point."; | ||||
|           } | ||||
|  | ||||
|           leaf out-voa-target { | ||||
|             // when "deref(../model)/has-output-voa"; FIXME: implement this | ||||
|             type tip-pe:db-ratio; | ||||
|             description "Output VOA setting | ||||
|  | ||||
|             If not set, GNPy will try to find an optimal operating point -- which means operating the EDFA at its highest gain | ||||
|             for the lowest NF, and using the output VOA to compensate."; | ||||
|           } | ||||
|  | ||||
|           leaf tilt-target { | ||||
|             type tip-pe:db-ratio; | ||||
|             // FIXME: make this available only when the amplifier model supports tilt settings | ||||
|             description "Desired tilt of the amplifier"; | ||||
|           } | ||||
|  | ||||
|           leaf delta-p { | ||||
|             type tip-pe:db-ratio; | ||||
|             description "FIXME: GNPy magic parameter."; // FIXME: describe this | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       case attenuator { | ||||
|         container attenuator { | ||||
|           description "Excessive attenuation | ||||
|  | ||||
|           Use this construct for slicing together longer segments of fiber. For shorter connections, | ||||
|           use an `nt:link` with a `patch`. Do not put an `attenuator` in between of two `nt:link`, `patch` connections."; | ||||
|  | ||||
|           leaf attenuation { | ||||
|             type tip-pe:db-ratio; | ||||
|             default 0; | ||||
|             description "Attenuator loss"; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       case transceiver { | ||||
|         container transceiver { | ||||
|           description "Transceiver"; | ||||
|  | ||||
|           leaf model { | ||||
|             type leafref { | ||||
|               path "/tip-pe:transceiver/tip-pe:type"; | ||||
|             } | ||||
|             mandatory true; | ||||
|             description "Transceiver model, a cross-reference to the equipment library"; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       case roadm { | ||||
|         container roadm { | ||||
|           description "ROADM"; | ||||
|  | ||||
|           leaf model { | ||||
|             type leafref { | ||||
|               path "/tip-pe:roadm/tip-pe:type"; | ||||
|             } | ||||
|             mandatory true; | ||||
|             description "ROADM model, a cross-reference to the equipment library"; | ||||
|           } | ||||
|  | ||||
|           leaf target-egress-per-channel-power { | ||||
|             type tip-pe:power; | ||||
|             description "Per-channel target egress power for signals exiting the ROADM | ||||
|  | ||||
|             This can be overriden on a per-link basis via patch/roadm-target-egress-per-channel-power."; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										55
									
								
								gnpy/yang/yanglib.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								gnpy/yang/yanglib.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| { | ||||
|   "ietf-yang-library:modules-state": { | ||||
|     "module-set-id": "", | ||||
|     "module": [ | ||||
|       { | ||||
|         "name": "ietf-inet-types", | ||||
|         "revision": "2013-07-15", | ||||
|         "namespace": "urn:ietf:params:xml:ns:yang:ietf-inet-types", | ||||
|         "conformance-type": "import" | ||||
|       }, | ||||
|       { | ||||
|         "name": "ietf-yang-types", | ||||
|         "revision": "2013-07-15", | ||||
|         "namespace": "urn:ietf:params:xml:ns:yang:ietf-yang-types", | ||||
|         "conformance-type": "import" | ||||
|       }, | ||||
|       { | ||||
|         "name": "ietf-network-topology", | ||||
|         "revision": "2018-02-26", | ||||
|         "namespace": "urn:ietf:params:xml:ns:yang:ietf-network-topology", | ||||
|         "conformance-type": "implement" | ||||
|       }, | ||||
|       { | ||||
|         "name": "ietf-network", | ||||
|         "revision": "2018-02-26", | ||||
|         "namespace": "urn:ietf:params:xml:ns:yang:ietf-network", | ||||
|         "conformance-type": "implement" | ||||
|       }, | ||||
|       { | ||||
|         "name": "tip-photonic-equipment", | ||||
|         "revision": "2020-09-01", | ||||
|         "namespace": "https://oopt.telecominfraproject.com/yang/equipment", | ||||
|         "conformance-type": "implement" | ||||
|       }, | ||||
|       { | ||||
|         "name": "tip-photonic-topology", | ||||
|         "revision": "2020-09-01", | ||||
|         "namespace": "https://oopt.telecominfraproject.com/yang/topology", | ||||
|         "conformance-type": "implement" | ||||
|       }, | ||||
|       { | ||||
|         "name": "tip-photonic-simulation", | ||||
|         "revision": "2020-09-01", | ||||
|         "namespace": "https://oopt.telecominfraproject.com/yang/simulation", | ||||
|         "conformance-type": "implement" | ||||
|       }, | ||||
|       { | ||||
|         "name": "tip-onos-topology", | ||||
|         "revision": "2021-06-06", | ||||
|         "namespace": "https://oopt.telecominfraproject.com/yang/onos-topology", | ||||
|         "conformance-type": "implement" | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
| } | ||||
| @@ -1,7 +1,10 @@ | ||||
| flask>=2.0.1,<3 | ||||
| matplotlib>=3.3.3,<4 | ||||
| networkx>=2.5,<3 | ||||
| numpy>=1.19.4,<2 | ||||
| pandas>=1.1.5,<2 | ||||
| pbr>=5.5.1,<6 | ||||
| pyang>=2.4.0,<3 | ||||
| scipy>=1.5.4,<2 | ||||
| xlrd>=1.2.0,<2 | ||||
| yangson>=1.4.8,<2 | ||||
|   | ||||
| @@ -10,7 +10,7 @@ home-page = https://github.com/Telecominfraproject/oopt-gnpy | ||||
| project_urls = | ||||
|     Bug Tracker = https://github.com/Telecominfraproject/oopt-gnpy/issues | ||||
|     Documentation = https://gnpy.readthedocs.io/ | ||||
| python-requires = >=3.6 | ||||
| python-requires = >=3.8 | ||||
| classifier = | ||||
|     Development Status :: 5 - Production/Stable | ||||
|     Intended Audience :: Developers | ||||
| @@ -20,8 +20,6 @@ classifier = | ||||
|     Natural Language :: English | ||||
|     Programming Language :: Python | ||||
|     Programming Language :: Python :: 3 :: Only | ||||
|     Programming Language :: Python :: 3.6 | ||||
|     Programming Language :: Python :: 3.7 | ||||
|     Programming Language :: Python :: 3.8 | ||||
|     Programming Language :: Python :: 3.9 | ||||
|     Programming Language :: Python :: Implementation :: CPython | ||||
| @@ -52,3 +50,4 @@ console_scripts = | ||||
|     gnpy-transmission-example = gnpy.tools.cli_examples:transmission_main_example | ||||
|     gnpy-path-request = gnpy.tools.cli_examples:path_requests_run | ||||
|     gnpy-convert-xls = gnpy.tools.convert:_do_convert | ||||
|     gnpy-convert-to-yang = gnpy.tools.cli_examples:convert_to_yang | ||||
|   | ||||
| @@ -34,6 +34,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 0, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "transceiver", | ||||
|                 "node-id": "trx Lorient_KMA", | ||||
|                 "link-tp-id": "trx Lorient_KMA" | ||||
|               } | ||||
| @@ -61,6 +62,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 3, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "ROADM", | ||||
|                 "node-id": "roadm Lorient_KMA", | ||||
|                 "link-tp-id": "roadm Lorient_KMA" | ||||
|               } | ||||
| @@ -79,6 +81,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 5, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "east edfa in Lorient_KMA to Vannes_KBE", | ||||
|                 "link-tp-id": "east edfa in Lorient_KMA to Vannes_KBE" | ||||
|               } | ||||
| @@ -115,6 +118,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 9, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "west edfa in Vannes_KBE to Lorient_KMA", | ||||
|                 "link-tp-id": "west edfa in Vannes_KBE to Lorient_KMA" | ||||
|               } | ||||
| @@ -133,6 +137,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 11, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "ROADM", | ||||
|                 "node-id": "roadm Vannes_KBE", | ||||
|                 "link-tp-id": "roadm Vannes_KBE" | ||||
|               } | ||||
| @@ -151,6 +156,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 13, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "transceiver", | ||||
|                 "node-id": "trx Vannes_KBE", | ||||
|                 "link-tp-id": "trx Vannes_KBE" | ||||
|               } | ||||
| @@ -211,6 +217,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 0, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "transceiver", | ||||
|                 "node-id": "trx Brest_KLA", | ||||
|                 "link-tp-id": "trx Brest_KLA" | ||||
|               } | ||||
| @@ -238,6 +245,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 3, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "ROADM", | ||||
|                 "node-id": "roadm Brest_KLA", | ||||
|                 "link-tp-id": "roadm Brest_KLA" | ||||
|               } | ||||
| @@ -256,6 +264,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 5, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "east edfa in Brest_KLA to Morlaix", | ||||
|                 "link-tp-id": "east edfa in Brest_KLA to Morlaix" | ||||
|               } | ||||
| @@ -328,6 +337,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 13, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "west edfa in Lannion_CAS to Morlaix", | ||||
|                 "link-tp-id": "west edfa in Lannion_CAS to Morlaix" | ||||
|               } | ||||
| @@ -346,6 +356,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 15, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "ROADM", | ||||
|                 "node-id": "roadm Lannion_CAS", | ||||
|                 "link-tp-id": "roadm Lannion_CAS" | ||||
|               } | ||||
| @@ -364,6 +375,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 17, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "east edfa in Lannion_CAS to Corlay", | ||||
|                 "link-tp-id": "east edfa in Lannion_CAS to Corlay" | ||||
|               } | ||||
| @@ -472,6 +484,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 29, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "west edfa in Lorient_KMA to Loudeac", | ||||
|                 "link-tp-id": "west edfa in Lorient_KMA to Loudeac" | ||||
|               } | ||||
| @@ -490,6 +503,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 31, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "ROADM", | ||||
|                 "node-id": "roadm Lorient_KMA", | ||||
|                 "link-tp-id": "roadm Lorient_KMA" | ||||
|               } | ||||
| @@ -508,6 +522,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 33, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "east edfa in Lorient_KMA to Vannes_KBE", | ||||
|                 "link-tp-id": "east edfa in Lorient_KMA to Vannes_KBE" | ||||
|               } | ||||
| @@ -544,6 +559,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 37, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "west edfa in Vannes_KBE to Lorient_KMA", | ||||
|                 "link-tp-id": "west edfa in Vannes_KBE to Lorient_KMA" | ||||
|               } | ||||
| @@ -562,6 +578,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 39, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "ROADM", | ||||
|                 "node-id": "roadm Vannes_KBE", | ||||
|                 "link-tp-id": "roadm Vannes_KBE" | ||||
|               } | ||||
| @@ -580,6 +597,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 41, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "transceiver", | ||||
|                 "node-id": "trx Vannes_KBE", | ||||
|                 "link-tp-id": "trx Vannes_KBE" | ||||
|               } | ||||
| @@ -640,6 +658,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 0, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "transceiver", | ||||
|                 "node-id": "trx Lannion_CAS", | ||||
|                 "link-tp-id": "trx Lannion_CAS" | ||||
|               } | ||||
| @@ -667,6 +686,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 3, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "ROADM", | ||||
|                 "node-id": "roadm Lannion_CAS", | ||||
|                 "link-tp-id": "roadm Lannion_CAS" | ||||
|               } | ||||
| @@ -685,6 +705,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 5, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "east edfa in Lannion_CAS to Stbrieuc", | ||||
|                 "link-tp-id": "east edfa in Lannion_CAS to Stbrieuc" | ||||
|               } | ||||
| @@ -721,6 +742,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 9, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "east edfa in Stbrieuc to Rennes_STA", | ||||
|                 "link-tp-id": "east edfa in Stbrieuc to Rennes_STA" | ||||
|               } | ||||
| @@ -757,6 +779,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 13, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "west edfa in Rennes_STA to Stbrieuc", | ||||
|                 "link-tp-id": "west edfa in Rennes_STA to Stbrieuc" | ||||
|               } | ||||
| @@ -775,6 +798,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 15, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "ROADM", | ||||
|                 "node-id": "roadm Rennes_STA", | ||||
|                 "link-tp-id": "roadm Rennes_STA" | ||||
|               } | ||||
| @@ -793,6 +817,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 17, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "transceiver", | ||||
|                 "node-id": "trx Rennes_STA", | ||||
|                 "link-tp-id": "trx Rennes_STA" | ||||
|               } | ||||
| @@ -853,6 +878,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 0, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "transceiver", | ||||
|                 "node-id": "trx Rennes_STA", | ||||
|                 "link-tp-id": "trx Rennes_STA" | ||||
|               } | ||||
| @@ -880,6 +906,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 3, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "ROADM", | ||||
|                 "node-id": "roadm Rennes_STA", | ||||
|                 "link-tp-id": "roadm Rennes_STA" | ||||
|               } | ||||
| @@ -898,6 +925,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 5, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "east edfa in Rennes_STA to Ploermel", | ||||
|                 "link-tp-id": "east edfa in Rennes_STA to Ploermel" | ||||
|               } | ||||
| @@ -934,6 +962,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 9, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "east edfa in Ploermel to Vannes_KBE", | ||||
|                 "link-tp-id": "east edfa in Ploermel to Vannes_KBE" | ||||
|               } | ||||
| @@ -970,6 +999,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 13, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "west edfa in Vannes_KBE to Ploermel", | ||||
|                 "link-tp-id": "west edfa in Vannes_KBE to Ploermel" | ||||
|               } | ||||
| @@ -988,6 +1018,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 15, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "ROADM", | ||||
|                 "node-id": "roadm Vannes_KBE", | ||||
|                 "link-tp-id": "roadm Vannes_KBE" | ||||
|               } | ||||
| @@ -1006,6 +1037,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 17, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "east edfa in Vannes_KBE to Lorient_KMA", | ||||
|                 "link-tp-id": "east edfa in Vannes_KBE to Lorient_KMA" | ||||
|               } | ||||
| @@ -1042,6 +1074,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 21, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "west edfa in Lorient_KMA to Vannes_KBE", | ||||
|                 "link-tp-id": "west edfa in Lorient_KMA to Vannes_KBE" | ||||
|               } | ||||
| @@ -1060,6 +1093,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 23, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "ROADM", | ||||
|                 "node-id": "roadm Lorient_KMA", | ||||
|                 "link-tp-id": "roadm Lorient_KMA" | ||||
|               } | ||||
| @@ -1078,6 +1112,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 25, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "east edfa in Lorient_KMA to Loudeac", | ||||
|                 "link-tp-id": "east edfa in Lorient_KMA to Loudeac" | ||||
|               } | ||||
| @@ -1186,6 +1221,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 37, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "west edfa in Lannion_CAS to Corlay", | ||||
|                 "link-tp-id": "west edfa in Lannion_CAS to Corlay" | ||||
|               } | ||||
| @@ -1204,6 +1240,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 39, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "ROADM", | ||||
|                 "node-id": "roadm Lannion_CAS", | ||||
|                 "link-tp-id": "roadm Lannion_CAS" | ||||
|               } | ||||
| @@ -1222,6 +1259,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 41, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "transceiver", | ||||
|                 "node-id": "trx Lannion_CAS", | ||||
|                 "link-tp-id": "trx Lannion_CAS" | ||||
|               } | ||||
| @@ -1282,6 +1320,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 0, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "transceiver", | ||||
|                 "node-id": "trx Rennes_STA", | ||||
|                 "link-tp-id": "trx Rennes_STA" | ||||
|               } | ||||
| @@ -1309,6 +1348,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 3, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "ROADM", | ||||
|                 "node-id": "roadm Rennes_STA", | ||||
|                 "link-tp-id": "roadm Rennes_STA" | ||||
|               } | ||||
| @@ -1327,6 +1367,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 5, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "east edfa in Rennes_STA to Stbrieuc", | ||||
|                 "link-tp-id": "east edfa in Rennes_STA to Stbrieuc" | ||||
|               } | ||||
| @@ -1363,6 +1404,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 9, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "west edfa in Stbrieuc to Rennes_STA", | ||||
|                 "link-tp-id": "west edfa in Stbrieuc to Rennes_STA" | ||||
|               } | ||||
| @@ -1399,6 +1441,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 13, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "EDFA", | ||||
|                 "node-id": "west edfa in Lannion_CAS to Stbrieuc", | ||||
|                 "link-tp-id": "west edfa in Lannion_CAS to Stbrieuc" | ||||
|               } | ||||
| @@ -1417,6 +1460,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 15, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "ROADM", | ||||
|                 "node-id": "roadm Lannion_CAS", | ||||
|                 "link-tp-id": "roadm Lannion_CAS" | ||||
|               } | ||||
| @@ -1435,6 +1479,7 @@ | ||||
|             "path-route-object": { | ||||
|               "index": 17, | ||||
|               "num-unnum-hop": { | ||||
|                 "gnpy-node-type": "transceiver", | ||||
|                 "node-id": "trx Lannion_CAS", | ||||
|                 "link-tp-id": "trx Lannion_CAS" | ||||
|               } | ||||
|   | ||||
| @@ -16,6 +16,15 @@ SRC_ROOT = Path(__file__).parent.parent | ||||
|      ['gnpy/example-data/raman_edfa_example_network.json', '--sim', 'gnpy/example-data/sim_params.json', '--show-channels', ]), | ||||
|     ('openroadm-Stockholm-Gothenburg', transmission_main_example, | ||||
|      ['-e', 'gnpy/example-data/eqpt_config_openroadm.json', 'gnpy/example-data/Sweden_OpenROADM_example_network.json', ]), | ||||
|     pytest.param('transmission_main_example', transmission_main_example, | ||||
|                  ['--from-yang', 'tests/yang/converted/edfa_example.json'], | ||||
|                  id='yang-transmission_main_example-edfa_example'), | ||||
|     pytest.param('openroadm-Stockholm-Gothenburg', transmission_main_example, | ||||
|                  ['--from-yang', 'tests/yang/converted/Sweden_OpenROADM_example.json'], | ||||
|                  id='yang-openroadm-sweden', | ||||
|                  # FIXME: investigate this | ||||
|                  marks=pytest.mark.xfail(reason='Different output: 0.01dB in one NF, and 0.03ps/nm in the final CD'), | ||||
|                  ), | ||||
| )) | ||||
| def test_example_invocation(capfdbinary, output, handler, args): | ||||
|     '''Make sure that our examples produce useful output''' | ||||
| @@ -23,7 +32,7 @@ def test_example_invocation(capfdbinary, output, handler, args): | ||||
|     expected = open(SRC_ROOT / 'tests' / 'invocation' / output, mode='rb').read() | ||||
|     handler(args) | ||||
|     captured = capfdbinary.readouterr() | ||||
|     assert captured.out == expected | ||||
|     assert captured.out.decode('utf-8') == expected.decode('utf-8') | ||||
|     assert captured.err == b'' | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -243,36 +243,8 @@ def test_csv_response_generation(tmpdir, json_input): | ||||
|         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 | ||||
|     """ | ||||
|     print(exp_resp) | ||||
|     print(act_resp) | ||||
|     test = True | ||||
|     for key in act_resp.keys(): | ||||
|         if key not in exp_resp.keys(): | ||||
|             print(f'{key} is not expected') | ||||
|             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 key not in act_resp.keys(): | ||||
|                 print(f'{key} is expected') | ||||
|                 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]: | ||||
|                 print(f'expected value :{exp_resp[key]}\n actual value: {act_resp[key]}') | ||||
|                 return False | ||||
|     return test | ||||
|  | ||||
|  | ||||
| # test json answers creation | ||||
| @pytest.mark.xfail  # FIXME: regenerate that file | ||||
| @pytest.mark.parametrize('xls_input, expected_response_file', { | ||||
|     DATA_DIR / 'testTopology.xls': DATA_DIR / 'testTopology_response.json', | ||||
| }.items()) | ||||
| @@ -327,7 +299,7 @@ def test_json_response_generation(xls_input, expected_response_file): | ||||
|         if i == 2: | ||||
|             # compare response must be False because z-a metric is missing | ||||
|             # (request with bidir option to cover bidir case) | ||||
|             assert not compare_response(expected['response'][i], response) | ||||
|             assert expected['response'][i] != response | ||||
|             print(f'response {response["response-id"]} should not match') | ||||
|             expected['response'][2]['path-properties']['z-a-path-metric'] = [ | ||||
|                 {'metric-type': 'SNR-bandwidth', 'accumulative-value': 22.809999999999999}, | ||||
| @@ -338,8 +310,7 @@ def test_json_response_generation(xls_input, expected_response_file): | ||||
|                 {'metric-type': 'path_bandwidth', 'accumulative-value': 60000000000.0}] | ||||
|             # test should be OK now | ||||
|         else: | ||||
|             assert compare_response(expected['response'][i], response) | ||||
|             print(f'response {response["response-id"]} is not correct') | ||||
|             assert expected['response'][i] == response | ||||
|  | ||||
| # test the correspondance names dict in case of excel input | ||||
| # test that using the created json network still works with excel input | ||||
|   | ||||
							
								
								
									
										120
									
								
								tests/test_yang_validation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								tests/test_yang_validation.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| # SPDX-License-Identifier: BSD-3-Clause | ||||
| # | ||||
| # Tests for YANG models of GNPy | ||||
| # | ||||
| # Copyright (C) 2020 Telecom Infra Project and GNPy contributors | ||||
| # see LICENSE.md for a list of contributors | ||||
| # | ||||
|  | ||||
| from gnpy.tools.json_io import load_equipment, load_network | ||||
| from gnpy.yang import external_path, model_path | ||||
| from gnpy.yang.io import create_datamodel, load_from_yang, save_to_json | ||||
| from pathlib import Path | ||||
| from typing import List | ||||
| import pytest | ||||
| import subprocess | ||||
| import json | ||||
|  | ||||
|  | ||||
| SRC_ROOT = Path(__file__).parent.parent | ||||
|  | ||||
|  | ||||
| def _get_basename(filename: Path) -> str: | ||||
|     try: | ||||
|         return filename.name | ||||
|     except AttributeError: | ||||
|         return filename | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize("yang_model", external_path().glob('*.yang'), ids=_get_basename) | ||||
| def test_lint_external_yang(yang_model): | ||||
|     '''Run a basic linter on all third-party models''' | ||||
|     _validate_yang_model(yang_model, []) | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize("yang_model", model_path().glob('*.yang'), ids=_get_basename) | ||||
| def test_lint_gnpy_yang(yang_model): | ||||
|     '''Run a linter on GNPy's YANG models''' | ||||
|     _validate_yang_model(yang_model, ('--canonical', '--strict', '--lint')) | ||||
|  | ||||
|  | ||||
| def _validate_yang_model(filename: Path, options: List[str]): | ||||
|     '''Run actual validation''' | ||||
|     # I would have loved to use pyang programatically from here, but it seems that the API is really designed | ||||
|     # around that interactive use case where code just expects an OptParser as a part of the library context, | ||||
|     # etc. | ||||
|     # Given that I'm only interested in a simple pass/fail scenario, let's just invoke the linter as a standalone | ||||
|     # process and check if it screams. | ||||
|     proc = subprocess.run( | ||||
|         ('pyang', '-p', ':'.join((str(external_path()), str(model_path()))), *options, filename), | ||||
|         stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, universal_newlines=True) | ||||
|     assert proc.stderr == '' | ||||
|     assert proc.stdout == '' | ||||
|  | ||||
|  | ||||
| @pytest.fixture | ||||
| def _yangson_datamodel(): | ||||
|     return create_datamodel | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize("filename", | ||||
|                          list((Path(__file__).parent / 'yang').glob('*.json')) + [ | ||||
|                              SRC_ROOT / 'gnpy' / 'example-data' / '2021-demo' / 'yang-without-onos.json', | ||||
|                              SRC_ROOT / 'gnpy' / 'example-data' / '2021-demo' / 'yang.json', | ||||
|                          ], | ||||
|                          ids=_get_basename) | ||||
| def test_validate_yang_data(_yangson_datamodel, filename: Path): | ||||
|     '''Validate a JSON file against our YANG models''' | ||||
|     dm = _yangson_datamodel() | ||||
|     with open(filename, 'r') as f: | ||||
|         raw_json = json.load(f) | ||||
|     data = dm.from_raw(raw_json) | ||||
|     data.validate() | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize("expected_file, equipment_file, topology_file", ( | ||||
|     ("edfa_example.json", "gnpy/example-data/eqpt_config.json", "gnpy/example-data/edfa_example_network.json"), | ||||
|     ("Sweden_OpenROADM_example.json", "gnpy/example-data/eqpt_config_openroadm.json", "gnpy/example-data/Sweden_OpenROADM_example_network.json"), | ||||
| )) | ||||
| def test_conversion_to_yang(expected_file, equipment_file, topology_file): | ||||
|     '''Conversion from legacy JSON to self-contained YANG data''' | ||||
|     equipment = load_equipment(SRC_ROOT / equipment_file) | ||||
|     network = load_network(SRC_ROOT / topology_file, equipment) | ||||
|     data = save_to_json(equipment, network) | ||||
|     serialized = json.dumps(data, indent=2) + '\n'  # files were generated via print(), hence a newline | ||||
|     expected = open(SRC_ROOT / 'tests' / 'yang' / 'converted' / expected_file, mode='rb').read().decode('utf-8') | ||||
|     assert serialized == expected | ||||
|  | ||||
|     y_equipment, y_network = load_from_yang(data) | ||||
|  | ||||
|     assert set(equipment.keys()) == set(y_equipment.keys()) | ||||
|  | ||||
|     for meta in ['Span', 'SI', 'Edfa', 'Fiber', 'RamanFiber', 'Roadm', 'Transceiver']: | ||||
|         assert equipment[meta].keys() == y_equipment[meta].keys() | ||||
|         for name in equipment[meta].keys(): | ||||
|             print(f'{meta}: {name}') | ||||
|             thing = equipment[meta][name] | ||||
|             y_thing = y_equipment[meta][name] | ||||
|             assert type(thing) == type(y_thing) | ||||
|             assert set(thing.__dict__.keys()) == set(y_thing.__dict__.keys()) | ||||
|             # FIXME: some bits are missing, some are numerically different | ||||
|             # for attr in thing.__dict__: | ||||
|             #     try: | ||||
|             #         assert getattr(thing, attr) == getattr(y_thing, attr) | ||||
|             #     except AssertionError: | ||||
|             #         print(f'!!! different attribute: {meta}: {name} -> {attr}') | ||||
|             #         raise | ||||
|  | ||||
|     # network nodes: | ||||
|     # the order is unstable, and there "might" be duplicate UIDs | ||||
|     len(network.nodes()) == len(y_network.nodes()) | ||||
|     assert set(n.uid for n in network.nodes()) == set(n.uid for n in y_network.nodes()) | ||||
|  | ||||
|     # edges are simple, just check the UIDs and cardinality | ||||
|     assert set((e[0].uid, e[1].uid) for e in network.edges()) == set((e[0].uid, e[1].uid) for e in y_network.edges()) | ||||
|     assert len(network.edges()) == len(y_network.edges()) | ||||
|  | ||||
|     # for orig_node in network.nodes(): | ||||
|     #     y_node = next(x for x in y_network.nodes() if x.uid == orig_node.uid) | ||||
|     #     # FIXME: fails on metadata... | ||||
|     #     assert orig_node.to_json == y_node.to_json | ||||
							
								
								
									
										1
									
								
								tests/yang/00-empty.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/yang/00-empty.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| {} | ||||
							
								
								
									
										638
									
								
								tests/yang/01-dummy.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										638
									
								
								tests/yang/01-dummy.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,638 @@ | ||||
| { | ||||
|   "tip-photonic-equipment:amplifier": [ | ||||
|     { | ||||
|       "type": "fixed-27", | ||||
|       "gain-min": "27", | ||||
|       "gain-flatmax": "27", | ||||
|       "max-power-out": "21.9", | ||||
|       "polynomial-NF": { | ||||
|         "a": "0", | ||||
|         "b": "0", | ||||
|         "c": "0", | ||||
|         "d": "4.9" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "fixed-22", | ||||
|       "gain-min": "22", | ||||
|       "gain-flatmax": "22", | ||||
|       "max-power-out": "21.9", | ||||
|       "polynomial-NF": { | ||||
|         "a": "0", | ||||
|         "b": "0", | ||||
|         "c": "0", | ||||
|         "d": "4.8" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "vg-15-30", | ||||
|       "gain-min": "15", | ||||
|       "gain-flatmax": "26", | ||||
|       "dynamic-gain-tilt": [ | ||||
|         { | ||||
|           "frequency": "191.35", | ||||
|           "dynamic-gain-tilt": "0" | ||||
|         }, | ||||
|         { | ||||
|           "frequency": "196.1", | ||||
|           "dynamic-gain-tilt": "2.4" | ||||
|         } | ||||
|       ], | ||||
|       "max-power-out": "23", | ||||
|       "min-max-NF": { | ||||
|         "nf-min": "6.0", | ||||
|         "nf-max": "10.0" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "vg-8-16", | ||||
|       "gain-min": "8", | ||||
|       "gain-flatmax": "15", | ||||
|       "max-power-out": "23", | ||||
|       "min-max-NF": { | ||||
|         "nf-min": "6.5", | ||||
|         "nf-max": "11.0" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "dual--vg-15-30--vg-8-16", | ||||
|       "gain-min": "25.0", | ||||
|       "composite": { | ||||
|         "preamp": "vg-15-30", | ||||
|         "booster": "vg-8-16" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "Juniper-BoosterHG", | ||||
|       "gain-min": "10", | ||||
|       "gain-flatmax": "25", | ||||
|       "max-power-out": "21", | ||||
|       "frequency-min": "191.35", | ||||
|       "frequency-max": "196.1", | ||||
|       "polynomial-NF": { | ||||
|         "a": "0.0008", | ||||
|         "b": "0.0272", | ||||
|         "c": "-0.2249", | ||||
|         "d": "6.4902" | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "tip-photonic-equipment:fiber": [ | ||||
|     { | ||||
|       "type": "SSMF", | ||||
|       "chromatic-dispersion": "16.7", | ||||
|       "gamma": "1.27", | ||||
|       "pmd-coefficient": "0.0400028124", | ||||
|       "raman-efficiency": [ | ||||
|         { | ||||
|           "delta-frequency": "0.0", | ||||
|           "cr": "0" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "0.5", | ||||
|           "cr": "9.4e-06" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "1.0", | ||||
|           "cr": "2.92e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "1.5", | ||||
|           "cr": "4.88e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "2.0", | ||||
|           "cr": "6.82e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "2.5", | ||||
|           "cr": "8.31e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "3.0", | ||||
|           "cr": "9.4e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "3.5", | ||||
|           "cr": "0.0001014" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "4.0", | ||||
|           "cr": "0.0001069" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "4.5", | ||||
|           "cr": "0.0001119" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "5.0", | ||||
|           "cr": "0.0001217" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "5.5", | ||||
|           "cr": "0.0001268" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "6.0", | ||||
|           "cr": "0.0001365" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "6.5", | ||||
|           "cr": "0.000149" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "7.0", | ||||
|           "cr": "0.000165" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "7.5", | ||||
|           "cr": "0.000181" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "8.0", | ||||
|           "cr": "0.0001977" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "8.5", | ||||
|           "cr": "0.0002192" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "9.0", | ||||
|           "cr": "0.0002469" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "9.5", | ||||
|           "cr": "0.0002749" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "10.0", | ||||
|           "cr": "0.0002999" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "10.5", | ||||
|           "cr": "0.0003206" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "11.0", | ||||
|           "cr": "0.0003405" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "11.5", | ||||
|           "cr": "0.0003592" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "12.0", | ||||
|           "cr": "0.000374" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "12.5", | ||||
|           "cr": "0.0003826" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "12.75", | ||||
|           "cr": "0.0003841" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "13.0", | ||||
|           "cr": "0.0003826" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "13.25", | ||||
|           "cr": "0.0003802" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "13.5", | ||||
|           "cr": "0.0003756" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "14.0", | ||||
|           "cr": "0.0003549" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "14.5", | ||||
|           "cr": "0.0003795" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "14.75", | ||||
|           "cr": "0.000344" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "15.0", | ||||
|           "cr": "0.0002933" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "15.5", | ||||
|           "cr": "0.0002024" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "16.0", | ||||
|           "cr": "0.0001158" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "16.5", | ||||
|           "cr": "8.46e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "17.0", | ||||
|           "cr": "7.14e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "17.5", | ||||
|           "cr": "6.86e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "18.0", | ||||
|           "cr": "8.5e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "18.25", | ||||
|           "cr": "8.93e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "18.5", | ||||
|           "cr": "9.01e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "18.75", | ||||
|           "cr": "8.15e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "19.0", | ||||
|           "cr": "6.67e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "19.5", | ||||
|           "cr": "4.37e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "20.0", | ||||
|           "cr": "3.28e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "20.5", | ||||
|           "cr": "2.96e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "21.0", | ||||
|           "cr": "2.65e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "21.5", | ||||
|           "cr": "2.57e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "22.0", | ||||
|           "cr": "2.81e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "22.5", | ||||
|           "cr": "3.08e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "23.0", | ||||
|           "cr": "3.67e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "23.5", | ||||
|           "cr": "5.85e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "24.0", | ||||
|           "cr": "6.63e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "24.5", | ||||
|           "cr": "6.36e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "25.0", | ||||
|           "cr": "5.5e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "25.5", | ||||
|           "cr": "4.06e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "26.0", | ||||
|           "cr": "2.77e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "26.5", | ||||
|           "cr": "2.42e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "27.0", | ||||
|           "cr": "1.87e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "27.5", | ||||
|           "cr": "1.6e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "28.0", | ||||
|           "cr": "1.4e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "28.5", | ||||
|           "cr": "1.13e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "29.0", | ||||
|           "cr": "1.05e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "29.5", | ||||
|           "cr": "9.8e-06" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "30.0", | ||||
|           "cr": "9.8e-06" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "30.5", | ||||
|           "cr": "1.13e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "31.0", | ||||
|           "cr": "1.64e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "31.5", | ||||
|           "cr": "1.95e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "32.0", | ||||
|           "cr": "2.38e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "32.5", | ||||
|           "cr": "2.26e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "33.0", | ||||
|           "cr": "2.03e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "33.5", | ||||
|           "cr": "1.48e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "34.0", | ||||
|           "cr": "1.09e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "34.5", | ||||
|           "cr": "9.8e-06" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "35.0", | ||||
|           "cr": "1.05e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "35.5", | ||||
|           "cr": "1.17e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "36.0", | ||||
|           "cr": "1.25e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "36.5", | ||||
|           "cr": "1.21e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "37.0", | ||||
|           "cr": "1.09e-05" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "37.5", | ||||
|           "cr": "9.8e-06" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "38.0", | ||||
|           "cr": "8.2e-06" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "38.5", | ||||
|           "cr": "6.6e-06" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "39.0", | ||||
|           "cr": "4.7e-06" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "39.5", | ||||
|           "cr": "2.7e-06" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "40.0", | ||||
|           "cr": "1.9e-06" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "40.5", | ||||
|           "cr": "1.2e-06" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "41.0", | ||||
|           "cr": "4e-07" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "41.5", | ||||
|           "cr": "2e-07" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "42.0", | ||||
|           "cr": "1e-07" | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "type": "NZDF", | ||||
|       "chromatic-dispersion": "5.0", | ||||
|       "gamma": "1.46", | ||||
|       "pmd-coefficient": "0.0400028124" | ||||
|     }, | ||||
|     { | ||||
|       "type": "LOF", | ||||
|       "chromatic-dispersion": "22", | ||||
|       "gamma": "0.843", | ||||
|       "pmd-coefficient": "0.0400028124" | ||||
|     } | ||||
|   ], | ||||
|   "tip-photonic-equipment:transceiver": [ | ||||
|     { | ||||
|       "type": "Voyager", | ||||
|       "mode": [ | ||||
|         { | ||||
|           "name": "DP-QPSK", | ||||
|           "bit-rate": 100, | ||||
|           "baud-rate": "32", | ||||
|           "required-osnr": "12", | ||||
|           "in-band-tx-osnr": "40", | ||||
|           "grid-spacing": "37.5", | ||||
|           "tx-roll-off": "0.15" | ||||
|         } | ||||
|       ] | ||||
|     } | ||||
|   ], | ||||
|   "tip-photonic-equipment:roadm": [ | ||||
|     { | ||||
|       "type": "default", | ||||
|       "add-drop-osnr": "50.0", | ||||
|       "target-channel-out-power": "-20", | ||||
|       "polarization-mode-dispersion": "0" | ||||
|     } | ||||
|   ], | ||||
|  | ||||
|   "ietf-network:networks": { | ||||
|     "network": [ | ||||
|       { | ||||
|         "network-id": "TIP OOPT-PSE sample topology", | ||||
|         "network-types": { | ||||
|           "tip-photonic-topology:photonic-topology": { | ||||
|           } | ||||
|         }, | ||||
|         "node": [ | ||||
|           { | ||||
|             "node-id": "trx-1", | ||||
|             "tip-photonic-topology:transceiver": { | ||||
|               "model": "Voyager" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "node-id": "trx-2", | ||||
|             "tip-photonic-topology:transceiver": { | ||||
|               "model": "Voyager" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "node-id": "edfa-A", | ||||
|             "tip-photonic-topology:amplifier": { | ||||
|               "model": "fixed-22", | ||||
|               "gain-target": "19.0", | ||||
|               "tilt-target": "10.0" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "node-id": "trx-100", | ||||
|             "tip-photonic-topology:transceiver": { | ||||
|               "model": "Voyager" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "node-id": "trx-101", | ||||
|             "tip-photonic-topology:transceiver": { | ||||
|               "model": "Voyager" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "node-id": "trx-200", | ||||
|             "tip-photonic-topology:transceiver": { | ||||
|               "model": "Voyager" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "node-id": "trx-201", | ||||
|             "tip-photonic-topology:transceiver": { | ||||
|               "model": "Voyager" | ||||
|             } | ||||
|           } | ||||
|         ], | ||||
|  | ||||
|         "ietf-network-topology:link": [ | ||||
|           { | ||||
|             "link-id": "fiber trx-1 edfa-A", | ||||
|             "source": { | ||||
|               "source-node": "trx-1" | ||||
|             }, | ||||
|             "destination": { | ||||
|               "dest-node": "edfa-A" | ||||
|             }, | ||||
|             "tip-photonic-topology:fiber": { | ||||
|               "type": "SSMF", | ||||
|               "length": "60" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "link-id": "fiber edfa-A trx-2", | ||||
|             "source": { | ||||
|               "source-node": "edfa-A" | ||||
|             }, | ||||
|             "destination": { | ||||
|               "dest-node": "trx-2" | ||||
|             }, | ||||
|             "tip-photonic-topology:fiber": { | ||||
|               "type": "SSMF", | ||||
|               "length": "50" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "link-id": "tentative link for autodesign #1", | ||||
|             "source": { | ||||
|               "source-node": "trx-100" | ||||
|             }, | ||||
|             "destination": { | ||||
|               "dest-node": "trx-101" | ||||
|             }, | ||||
|             "tip-photonic-topology:tentative-link": { | ||||
|               "type": "SSMF", | ||||
|               "length": "200" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "link-id": "A Raman fiber", | ||||
|             "source": { | ||||
|               "source-node": "trx-200" | ||||
|             }, | ||||
|             "destination": { | ||||
|               "dest-node": "trx-201" | ||||
|             }, | ||||
|             "tip-photonic-topology:fiber": { | ||||
|               "type": "SSMF", | ||||
|               "length": "150", | ||||
|               "raman": { | ||||
|                 "temperature": 293, | ||||
|                 "pump": [ | ||||
|                   { | ||||
|                     "frequency": "205.0", | ||||
|                     "power": "0.2", | ||||
|                     "direction": "counter-propagating" | ||||
|                   }, | ||||
|                   { | ||||
|                     "frequency": "201.0", | ||||
|                     "power": "0.25", | ||||
|                     "direction": "counter-propagating" | ||||
|                   } | ||||
|                 ] | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|  | ||||
|   "tip-photonic-simulation:simulation": { | ||||
|     "autodesign": { | ||||
|       "power-adjustment-for-span-loss": { | ||||
|         "maximal-reduction": "-2.0", | ||||
|         "maximal-boost": "3.0", | ||||
|         "excursion-step-size": "0.5" | ||||
|       }, | ||||
|       "gain-mode": [null] | ||||
|     }, | ||||
|     "grid": { | ||||
|       "frequency-min": "191.8", | ||||
|       "frequency-max": "195.5", | ||||
|       "spacing": "50", | ||||
|       "baud-rate": "37.5", | ||||
|       "tx-roll-off": "0.5", | ||||
|       "tx-osnr": "40", | ||||
|       "power": "0" | ||||
|     }, | ||||
|     "nli": { | ||||
|       "raman": { | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										135
									
								
								tests/yang/02-simple.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								tests/yang/02-simple.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,135 @@ | ||||
| { | ||||
|   "tip-photonic-equipment:amplifier": [ | ||||
|     { | ||||
|       "type": "fixed-22", | ||||
|       "gain-min": "22", | ||||
|       "gain-flatmax": "22", | ||||
|       "max-power-out": "21.9", | ||||
|       "polynomial-NF": { | ||||
|         "a": "0", | ||||
|         "b": "0", | ||||
|         "c": "0", | ||||
|         "d": "4.8" | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "tip-photonic-equipment:fiber": [ | ||||
|     { | ||||
|       "type": "SSMF", | ||||
|       "chromatic-dispersion": "16.7", | ||||
|       "gamma": "1.27", | ||||
|       "pmd-coefficient": "0.0400028124" | ||||
|     } | ||||
|   ], | ||||
|   "tip-photonic-equipment:transceiver": [ | ||||
|     { | ||||
|       "type": "Voyager", | ||||
|       "mode": [ | ||||
|         { | ||||
|           "name": "DP-QPSK", | ||||
|           "bit-rate": 100, | ||||
|           "baud-rate": "32.0", | ||||
|           "required-osnr": "12", | ||||
|           "in-band-tx-osnr": "40", | ||||
|           "grid-spacing": "37.5", | ||||
|           "tx-roll-off": "0.15" | ||||
|         } | ||||
|       ] | ||||
|     } | ||||
|   ], | ||||
|   "tip-photonic-equipment:roadm": [ | ||||
|     { | ||||
|       "type": "default", | ||||
|       "add-drop-osnr": "50.0", | ||||
|       "target-channel-out-power": "-20", | ||||
|       "polarization-mode-dispersion": "0" | ||||
|     } | ||||
|   ], | ||||
|  | ||||
|   "ietf-network:networks": { | ||||
|     "network": [ | ||||
|       { | ||||
|         "network-id": "Two transponders, one EDFA, two fibers", | ||||
|         "network-types": { | ||||
|           "tip-photonic-topology:photonic-topology": { | ||||
|           } | ||||
|         }, | ||||
|         "node": [ | ||||
|           { | ||||
|             "node-id": "trx-1", | ||||
|             "tip-photonic-topology:transceiver": { | ||||
|               "model": "Voyager" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "node-id": "trx-2", | ||||
|             "tip-photonic-topology:transceiver": { | ||||
|               "model": "Voyager" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "node-id": "edfa-A", | ||||
|             "tip-photonic-topology:amplifier": { | ||||
|               "model": "fixed-22", | ||||
|               "gain-target": "19.0", | ||||
|               "tilt-target": "10.0" | ||||
|             } | ||||
|           } | ||||
|         ], | ||||
|         "ietf-network-topology:link": [ | ||||
|           { | ||||
|             "link-id": "fiber trx-1 edfa-1", | ||||
|             "source": { | ||||
|               "source-node": "trx-1" | ||||
|             }, | ||||
|             "destination": { | ||||
|               "dest-node": "edfa-A" | ||||
|             }, | ||||
|             "tip-photonic-topology:fiber": { | ||||
|               "type": "SSMF", | ||||
|               "length": "105" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "link-id": "fiber trx-2", | ||||
|             "source": { | ||||
|               "source-node": "edfa-A" | ||||
|             }, | ||||
|             "destination": { | ||||
|               "dest-node": "trx-2" | ||||
|             }, | ||||
|             "tip-photonic-topology:fiber": { | ||||
|               "type": "SSMF", | ||||
|               "length": "70" | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   "tip-photonic-simulation:simulation": { | ||||
|     "autodesign": { | ||||
|       "power-adjustment-for-span-loss": { | ||||
|         "maximal-reduction": "-2.0", | ||||
|         "maximal-boost": "3.0", | ||||
|         "excursion-step-size": "0.5" | ||||
|       }, | ||||
|       "power-mode": { | ||||
|         "power-sweep": { | ||||
|           "start": "0.0", | ||||
|           "stop": "0.0", | ||||
|           "step-size": "1.0" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "grid": { | ||||
|       "frequency-min": "191.3", | ||||
|       "frequency-max": "195.1", | ||||
|       "spacing": "50.0", | ||||
|       "baud-rate": "37.5", | ||||
|       "tx-roll-off": "0.5", | ||||
|       "tx-osnr": "40", | ||||
|       "power": "0" | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										4826
									
								
								tests/yang/converted/Sweden_OpenROADM_example.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4826
									
								
								tests/yang/converted/Sweden_OpenROADM_example.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										771
									
								
								tests/yang/converted/edfa_example.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										771
									
								
								tests/yang/converted/edfa_example.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,771 @@ | ||||
| { | ||||
|   "tip-photonic-equipment:amplifier": [ | ||||
|     { | ||||
|       "type": "high_detail_model_example", | ||||
|       "gain-min": "15.0", | ||||
|       "frequency-min": "191.35", | ||||
|       "frequency-max": "196.1", | ||||
|       "gain-flatmax": "25.0", | ||||
|       "max-power-out": "21.0", | ||||
|       "has-output-voa": false, | ||||
|       "polynomial-NF": { | ||||
|         "a": "0.000168241", | ||||
|         "b": "0.0469961", | ||||
|         "c": "0.0359549", | ||||
|         "d": "5.82851" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "Juniper_BoosterHG", | ||||
|       "gain-min": "10.0", | ||||
|       "frequency-min": "191.4", | ||||
|       "frequency-max": "196.1", | ||||
|       "gain-flatmax": "25.0", | ||||
|       "max-power-out": "21.0", | ||||
|       "has-output-voa": false, | ||||
|       "polynomial-NF": { | ||||
|         "a": "0.0008", | ||||
|         "b": "0.0272", | ||||
|         "c": "-0.2249", | ||||
|         "d": "6.4902" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "operator_model_example", | ||||
|       "gain-min": "15.0", | ||||
|       "frequency-min": "191.35", | ||||
|       "frequency-max": "196.1", | ||||
|       "gain-flatmax": "26.0", | ||||
|       "max-power-out": "23.0", | ||||
|       "has-output-voa": false, | ||||
|       "min-max-NF": { | ||||
|         "nf-min": "6.0", | ||||
|         "nf-max": "10.0" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "openroadm_ila_low_noise", | ||||
|       "gain-min": "0.0", | ||||
|       "frequency-min": "191.35", | ||||
|       "frequency-max": "196.1", | ||||
|       "gain-flatmax": "27.0", | ||||
|       "max-power-out": "22.0", | ||||
|       "has-output-voa": false, | ||||
|       "OpenROADM-ILA": { | ||||
|         "a": "-0.0008104", | ||||
|         "b": "-0.06221", | ||||
|         "c": "-0.5889", | ||||
|         "d": "37.62" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "openroadm_ila_standard", | ||||
|       "gain-min": "0.0", | ||||
|       "frequency-min": "191.35", | ||||
|       "frequency-max": "196.1", | ||||
|       "gain-flatmax": "27.0", | ||||
|       "max-power-out": "22.0", | ||||
|       "has-output-voa": false, | ||||
|       "OpenROADM-ILA": { | ||||
|         "a": "-0.0005952", | ||||
|         "b": "-0.0625", | ||||
|         "c": "-1.071", | ||||
|         "d": "28.99" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "openroadm_mw_mw_preamp", | ||||
|       "gain-min": "0.0", | ||||
|       "frequency-min": "191.35", | ||||
|       "frequency-max": "196.1", | ||||
|       "gain-flatmax": "27.0", | ||||
|       "max-power-out": "22.0", | ||||
|       "has-output-voa": false, | ||||
|       "OpenROADM-preamp": {} | ||||
|     }, | ||||
|     { | ||||
|       "type": "openroadm_mw_mw_booster", | ||||
|       "gain-min": "0.0", | ||||
|       "frequency-min": "191.35", | ||||
|       "frequency-max": "196.1", | ||||
|       "gain-flatmax": "32.0", | ||||
|       "max-power-out": "22.0", | ||||
|       "has-output-voa": false, | ||||
|       "OpenROADM-booster": {} | ||||
|     }, | ||||
|     { | ||||
|       "type": "std_high_gain", | ||||
|       "gain-min": "25.0", | ||||
|       "frequency-min": "191.35", | ||||
|       "frequency-max": "196.1", | ||||
|       "gain-flatmax": "35.0", | ||||
|       "max-power-out": "21.0", | ||||
|       "has-output-voa": false, | ||||
|       "min-max-NF": { | ||||
|         "nf-min": "5.5", | ||||
|         "nf-max": "7.0" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "std_medium_gain", | ||||
|       "gain-min": "15.0", | ||||
|       "frequency-min": "191.35", | ||||
|       "frequency-max": "196.1", | ||||
|       "gain-flatmax": "26.0", | ||||
|       "max-power-out": "23.0", | ||||
|       "has-output-voa": false, | ||||
|       "min-max-NF": { | ||||
|         "nf-min": "6.0", | ||||
|         "nf-max": "10.0" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "std_low_gain", | ||||
|       "gain-min": "8.0", | ||||
|       "frequency-min": "191.35", | ||||
|       "frequency-max": "196.1", | ||||
|       "gain-flatmax": "16.0", | ||||
|       "max-power-out": "23.0", | ||||
|       "has-output-voa": false, | ||||
|       "min-max-NF": { | ||||
|         "nf-min": "6.5", | ||||
|         "nf-max": "11.0" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "high_power", | ||||
|       "gain-min": "8.0", | ||||
|       "frequency-min": "191.35", | ||||
|       "frequency-max": "196.1", | ||||
|       "gain-flatmax": "16.0", | ||||
|       "max-power-out": "25.0", | ||||
|       "has-output-voa": false, | ||||
|       "min-max-NF": { | ||||
|         "nf-min": "9.0", | ||||
|         "nf-max": "15.0" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "std_fixed_gain", | ||||
|       "gain-min": "20.0", | ||||
|       "frequency-min": "191.35", | ||||
|       "frequency-max": "196.1", | ||||
|       "gain-flatmax": "21.0", | ||||
|       "max-power-out": "21.0", | ||||
|       "has-output-voa": false, | ||||
|       "polynomial-NF": { | ||||
|         "a": "0.0", | ||||
|         "b": "0.0", | ||||
|         "c": "0.0", | ||||
|         "d": "5.5" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "4pumps_raman", | ||||
|       "gain-min": "12.0", | ||||
|       "frequency-min": "191.35", | ||||
|       "frequency-max": "196.1", | ||||
|       "gain-flatmax": "12.0", | ||||
|       "max-power-out": "21.0", | ||||
|       "has-output-voa": false, | ||||
|       "raman-approximation": { | ||||
|         "nf": "-1.0" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "hybrid_4pumps_lowgain", | ||||
|       "gain-min": "25.0", | ||||
|       "composite": { | ||||
|         "preamp": "4pumps_raman", | ||||
|         "booster": "std_low_gain" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "hybrid_4pumps_mediumgain", | ||||
|       "gain-min": "25.0", | ||||
|       "composite": { | ||||
|         "preamp": "4pumps_raman", | ||||
|         "booster": "std_medium_gain" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "medium+low_gain", | ||||
|       "gain-min": "25.0", | ||||
|       "composite": { | ||||
|         "preamp": "std_medium_gain", | ||||
|         "booster": "std_low_gain" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "medium+high_power", | ||||
|       "gain-min": "25.0", | ||||
|       "composite": { | ||||
|         "preamp": "std_medium_gain", | ||||
|         "booster": "high_power" | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "tip-photonic-equipment:fiber": [ | ||||
|     { | ||||
|       "type": "NZDF", | ||||
|       "chromatic-dispersion": "5.0", | ||||
|       "gamma": "1.46", | ||||
|       "pmd-coefficient": "0.0400028124" | ||||
|     }, | ||||
|     { | ||||
|       "type": "LOF", | ||||
|       "chromatic-dispersion": "22.0", | ||||
|       "gamma": "0.843", | ||||
|       "pmd-coefficient": "0.0400028124" | ||||
|     }, | ||||
|     { | ||||
|       "type": "SSMF", | ||||
|       "chromatic-dispersion": "16.7", | ||||
|       "gamma": "1.27", | ||||
|       "pmd-coefficient": "0.0400028124", | ||||
|       "raman-efficiency": [ | ||||
|         { | ||||
|           "delta-frequency": "0.0", | ||||
|           "cr": "0.0" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "0.5", | ||||
|           "cr": "0.0000094" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "1.0", | ||||
|           "cr": "0.0000292" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "1.5", | ||||
|           "cr": "0.0000488" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "2.0", | ||||
|           "cr": "0.0000682" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "2.5", | ||||
|           "cr": "0.0000831" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "3.0", | ||||
|           "cr": "0.000094" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "3.5", | ||||
|           "cr": "0.0001014" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "4.0", | ||||
|           "cr": "0.0001069" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "4.5", | ||||
|           "cr": "0.0001119" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "5.0", | ||||
|           "cr": "0.0001217" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "5.5", | ||||
|           "cr": "0.0001268" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "6.0", | ||||
|           "cr": "0.0001365" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "6.5", | ||||
|           "cr": "0.000149" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "7.0", | ||||
|           "cr": "0.000165" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "7.5", | ||||
|           "cr": "0.000181" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "8.0", | ||||
|           "cr": "0.0001977" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "8.5", | ||||
|           "cr": "0.0002192" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "9.0", | ||||
|           "cr": "0.0002469" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "9.5", | ||||
|           "cr": "0.0002749" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "10.0", | ||||
|           "cr": "0.0002999" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "10.5", | ||||
|           "cr": "0.0003206" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "11.0", | ||||
|           "cr": "0.0003405" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "11.5", | ||||
|           "cr": "0.0003592" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "12.0", | ||||
|           "cr": "0.000374" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "12.5", | ||||
|           "cr": "0.0003826" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "12.75", | ||||
|           "cr": "0.0003841" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "13.0", | ||||
|           "cr": "0.0003826" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "13.25", | ||||
|           "cr": "0.0003802" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "13.5", | ||||
|           "cr": "0.0003756" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "14.0", | ||||
|           "cr": "0.0003549" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "14.5", | ||||
|           "cr": "0.0003795" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "14.75", | ||||
|           "cr": "0.000344" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "15.0", | ||||
|           "cr": "0.0002933" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "15.5", | ||||
|           "cr": "0.0002024" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "16.0", | ||||
|           "cr": "0.0001158" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "16.5", | ||||
|           "cr": "0.0000846" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "17.0", | ||||
|           "cr": "0.0000714" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "17.5", | ||||
|           "cr": "0.0000686" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "18.0", | ||||
|           "cr": "0.000085" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "18.25", | ||||
|           "cr": "0.0000893" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "18.5", | ||||
|           "cr": "0.0000901" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "18.75", | ||||
|           "cr": "0.0000815" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "19.0", | ||||
|           "cr": "0.0000667" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "19.5", | ||||
|           "cr": "0.0000437" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "20.0", | ||||
|           "cr": "0.0000328" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "20.5", | ||||
|           "cr": "0.0000296" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "21.0", | ||||
|           "cr": "0.0000265" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "21.5", | ||||
|           "cr": "0.0000257" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "22.0", | ||||
|           "cr": "0.0000281" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "22.5", | ||||
|           "cr": "0.0000308" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "23.0", | ||||
|           "cr": "0.0000367" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "23.5", | ||||
|           "cr": "0.0000585" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "24.0", | ||||
|           "cr": "0.0000663" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "24.5", | ||||
|           "cr": "0.0000636" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "25.0", | ||||
|           "cr": "0.000055" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "25.5", | ||||
|           "cr": "0.0000406" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "26.0", | ||||
|           "cr": "0.0000277" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "26.5", | ||||
|           "cr": "0.0000242" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "27.0", | ||||
|           "cr": "0.0000187" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "27.5", | ||||
|           "cr": "0.000016" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "28.0", | ||||
|           "cr": "0.000014" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "28.5", | ||||
|           "cr": "0.0000113" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "29.0", | ||||
|           "cr": "0.0000105" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "29.5", | ||||
|           "cr": "0.0000098" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "30.0", | ||||
|           "cr": "0.0000098" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "30.5", | ||||
|           "cr": "0.0000113" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "31.0", | ||||
|           "cr": "0.0000164" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "31.5", | ||||
|           "cr": "0.0000195" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "32.0", | ||||
|           "cr": "0.0000238" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "32.5", | ||||
|           "cr": "0.0000226" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "33.0", | ||||
|           "cr": "0.0000203" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "33.5", | ||||
|           "cr": "0.0000148" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "34.0", | ||||
|           "cr": "0.0000109" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "34.5", | ||||
|           "cr": "0.0000098" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "35.0", | ||||
|           "cr": "0.0000105" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "35.5", | ||||
|           "cr": "0.0000117" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "36.0", | ||||
|           "cr": "0.0000125" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "36.5", | ||||
|           "cr": "0.0000121" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "37.0", | ||||
|           "cr": "0.0000109" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "37.5", | ||||
|           "cr": "0.0000098" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "38.0", | ||||
|           "cr": "0.0000082" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "38.5", | ||||
|           "cr": "0.0000066" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "39.0", | ||||
|           "cr": "0.0000047" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "39.5", | ||||
|           "cr": "0.0000027" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "40.0", | ||||
|           "cr": "0.0000019" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "40.5", | ||||
|           "cr": "0.0000012" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "41.0", | ||||
|           "cr": "4.00000E-7" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "41.5", | ||||
|           "cr": "2.00000E-7" | ||||
|         }, | ||||
|         { | ||||
|           "delta-frequency": "42.0", | ||||
|           "cr": "1.00000E-7" | ||||
|         } | ||||
|       ] | ||||
|     } | ||||
|   ], | ||||
|   "tip-photonic-equipment:transceiver": [ | ||||
|     { | ||||
|       "type": "vendorA_trx-type1", | ||||
|       "frequency-min": "191.35", | ||||
|       "frequency-max": "196.1", | ||||
|       "mode": [ | ||||
|         { | ||||
|           "name": "mode 1", | ||||
|           "bit-rate": 100, | ||||
|           "baud-rate": "32.0", | ||||
|           "required-osnr": "11.0", | ||||
|           "in-band-tx-osnr": "40.0", | ||||
|           "grid-spacing": "37.5", | ||||
|           "tx-roll-off": "0.15", | ||||
|           "tip-photonic-simulation:cost": 1 | ||||
|         }, | ||||
|         { | ||||
|           "name": "mode 2", | ||||
|           "bit-rate": 200, | ||||
|           "baud-rate": "66.0", | ||||
|           "required-osnr": "15.0", | ||||
|           "in-band-tx-osnr": "40.0", | ||||
|           "grid-spacing": "75.0", | ||||
|           "tx-roll-off": "0.15", | ||||
|           "tip-photonic-simulation:cost": 1 | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "type": "Voyager", | ||||
|       "frequency-min": "191.35", | ||||
|       "frequency-max": "196.1", | ||||
|       "mode": [ | ||||
|         { | ||||
|           "name": "mode 1", | ||||
|           "bit-rate": 100, | ||||
|           "baud-rate": "32.0", | ||||
|           "required-osnr": "12.0", | ||||
|           "in-band-tx-osnr": "40.0", | ||||
|           "grid-spacing": "37.5", | ||||
|           "tx-roll-off": "0.15", | ||||
|           "tip-photonic-simulation:cost": 1 | ||||
|         }, | ||||
|         { | ||||
|           "name": "mode 3", | ||||
|           "bit-rate": 300, | ||||
|           "baud-rate": "44.0", | ||||
|           "required-osnr": "18.0", | ||||
|           "in-band-tx-osnr": "40.0", | ||||
|           "grid-spacing": "62.5", | ||||
|           "tx-roll-off": "0.15", | ||||
|           "tip-photonic-simulation:cost": 1 | ||||
|         }, | ||||
|         { | ||||
|           "name": "mode 2", | ||||
|           "bit-rate": 400, | ||||
|           "baud-rate": "66.0", | ||||
|           "required-osnr": "21.0", | ||||
|           "in-band-tx-osnr": "40.0", | ||||
|           "grid-spacing": "75.0", | ||||
|           "tx-roll-off": "0.15", | ||||
|           "tip-photonic-simulation:cost": 1 | ||||
|         }, | ||||
|         { | ||||
|           "name": "mode 4", | ||||
|           "bit-rate": 200, | ||||
|           "baud-rate": "66.0", | ||||
|           "required-osnr": "16.0", | ||||
|           "in-band-tx-osnr": "40.0", | ||||
|           "grid-spacing": "75.0", | ||||
|           "tx-roll-off": "0.15", | ||||
|           "tip-photonic-simulation:cost": 1 | ||||
|         } | ||||
|       ] | ||||
|     } | ||||
|   ], | ||||
|   "tip-photonic-equipment:roadm": [ | ||||
|     { | ||||
|       "type": "default", | ||||
|       "add-drop-osnr": "38.0", | ||||
|       "polarization-mode-dispersion": "0.0", | ||||
|       "target-channel-out-power": "-20.0", | ||||
|       "compatible-preamp": [], | ||||
|       "compatible-booster": [] | ||||
|     } | ||||
|   ], | ||||
|   "tip-photonic-simulation:simulation": { | ||||
|     "grid": { | ||||
|       "frequency-min": "191.3", | ||||
|       "frequency-max": "195.1", | ||||
|       "spacing": "50.0", | ||||
|       "power": "0.0", | ||||
|       "tx-roll-off": "0.15", | ||||
|       "tx-osnr": "40.0", | ||||
|       "baud-rate": "32.0" | ||||
|     }, | ||||
|     "autodesign": { | ||||
|       "allowed-inline-edfa": [ | ||||
|         "std_high_gain", | ||||
|         "std_medium_gain", | ||||
|         "std_low_gain", | ||||
|         "hybrid_4pumps_lowgain", | ||||
|         "hybrid_4pumps_mediumgain", | ||||
|         "medium+low_gain" | ||||
|       ], | ||||
|       "power-adjustment-for-span-loss": { | ||||
|         "maximal-reduction": "-2.0", | ||||
|         "maximal-boost": "3.0", | ||||
|         "excursion-step-size": "0.5" | ||||
|       }, | ||||
|       "power-mode": { | ||||
|         "power-sweep": { | ||||
|           "start": "0.0", | ||||
|           "stop": "0.0", | ||||
|           "step-size": "1.0" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "system-margin": "2.0" | ||||
|   }, | ||||
|   "ietf-network:networks": { | ||||
|     "network": [ | ||||
|       { | ||||
|         "network-id": "GNPy", | ||||
|         "network-types": { | ||||
|           "tip-photonic-topology:photonic-topology": {} | ||||
|         }, | ||||
|         "node": [ | ||||
|           { | ||||
|             "node-id": "Site_A", | ||||
|             "tip-photonic-topology:transceiver": { | ||||
|               "model": "vendorA_trx-type1" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "node-id": "Edfa1", | ||||
|             "tip-photonic-topology:amplifier": { | ||||
|               "model": "std_low_gain", | ||||
|               "gain-target": "17.0", | ||||
|               "tilt-target": "0.0", | ||||
|               "out-voa-target": "0.0" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "node-id": "Site_B", | ||||
|             "tip-photonic-topology:transceiver": { | ||||
|               "model": "vendorA_trx-type1" | ||||
|             } | ||||
|           } | ||||
|         ], | ||||
|         "ietf-network-topology:link": [ | ||||
|           { | ||||
|             "link-id": "Span1", | ||||
|             "source": { | ||||
|               "source-node": "Site_A" | ||||
|             }, | ||||
|             "destination": { | ||||
|               "dest-node": "Edfa1" | ||||
|             }, | ||||
|             "tip-photonic-topology:fiber": { | ||||
|               "type": "SSMF", | ||||
|               "length": "80.0", | ||||
|               "attenuation-in": "0.0", | ||||
|               "conn-att-in": "0.5", | ||||
|               "conn-att-out": "0.5" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "link-id": "patch{Edfa1, Site_B}", | ||||
|             "source": { | ||||
|               "source-node": "Edfa1" | ||||
|             }, | ||||
|             "destination": { | ||||
|               "dest-node": "Site_B" | ||||
|             }, | ||||
|             "tip-photonic-topology:patch": {} | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user