From 2f3d5bf4d788148aa8fb7f7a5d298f53bedbeeaf Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 09:51:08 +0200 Subject: [PATCH 01/31] feat(otn)!: remove optical_transport and optical_multiplexer Both schemas covered the same domain and each warned against loading the other. They are replaced by extensions/otn, which models the optical transport network in its own namespace. BREAKING CHANGE: extensions/optical_multiplexer was a stable extension. Deployments loading it must migrate to extensions/otn. --- .metadata.yml | 19 - base/dcim.yml | 2 +- docs/docs/home.mdx | 2 - docs/docs/reference/optical_multiplexer.mdx | 461 ---- docs/docs/reference/optical_transport.mdx | 1953 ----------------- experimental/optical_transport/README.md | 3 - .../optical_transport/optical_transport.yml | 1500 ------------- extensions/optical_multiplexer/README.md | 3 - .../optical_multiplexer.yml | 323 --- extensions/patch_panel/patch_panel.yml | 5 +- .../optical_multiplexer.yml | 170 -- .../extensions/routing_pim/routing_pim.yml | 2 +- .../extensions/transceiver/transceiver.yml | 3 - 13 files changed, 4 insertions(+), 4442 deletions(-) delete mode 100644 docs/docs/reference/optical_multiplexer.mdx delete mode 100644 docs/docs/reference/optical_transport.mdx delete mode 100644 experimental/optical_transport/README.md delete mode 100644 experimental/optical_transport/optical_transport.yml delete mode 100644 extensions/optical_multiplexer/README.md delete mode 100644 extensions/optical_multiplexer/optical_multiplexer.yml delete mode 100644 objects/extensions/optical_multiplexer/optical_multiplexer.yml diff --git a/.metadata.yml b/.metadata.yml index e08f946e..29660995 100644 --- a/.metadata.yml +++ b/.metadata.yml @@ -76,14 +76,6 @@ experimental/modules_routing_engine: information like the version. You can insert the Routing Engine into a Dcim Physical Device and leverage the Routing Engine type model. name: Modules Routing Engine -experimental/optical_transport: - dependencies: - - base - - extensions/device_module - - extensions/cable - description: | - Comprehensive optical transport network schemas for DWDM/WDM systems (ADVA FSP 3000 and similar platforms). Covers four layers: wavelength (ITU-T G.694.1 grid, optical bands, DWDM channels), topology (logical optical nodes, passive multiplexers, fiber links), equipment (transponder/amplifier/ROADM modules, ROADM degrees, WSS cross-connects), and service (end-to-end optical services, optical paths, path segments). Not designed to be loaded together with extensions/optical_multiplexer. - name: Optical Transport experimental/qos: dependencies: - base @@ -229,17 +221,6 @@ extensions/mlag: Not covered yet: the layer 3 overlay for MLAG interfaces (loopback, peer address ...) and surfacing MLAG interfaces on each device in the domain. name: MLAG -extensions/optical_multiplexer: - dependencies: - - base - - extensions/transceiver - description: | - This schema extension models optical add-drop multiplexers (OADM) and the wavelength division multiplexing (WDM) channels they carry, for both CWDM and DWDM. It adds an Optical Multiplexer device with front and rear interfaces, a WDM Channel node holding channel number, wavelength and frequency, and a WDM Transceiver flavour of the transceiver model tuned to a channel. - - Some vendors configure tunable optics by wavelength or frequency rather than channel number; the WDM Channel node gives you a single entry in Infrahub for all three. - - Not designed to be loaded together with experimental/optical_transport, which covers the same domain in more depth. - name: Optical Multiplexer extensions/patch_panel: dependencies: - base diff --git a/base/dcim.yml b/base/dcim.yml index 58927bf4..0a87b8b6 100644 --- a/base/dcim.yml +++ b/base/dcim.yml @@ -67,7 +67,7 @@ generics: # Duplicated from DcimGenericDevice because generics cannot inherit generics. # DcimModuleBay.computed_name (extensions/device_module) renders # {{ device__name__value }} against peer DcimPhysicalDevice, so name must resolve here. - # allow_override lets DcimPatchPanel and DcimOpticalMultiplexer restate it. + # allow_override lets DcimPatchPanel restate it. - name: name kind: Text unique: true diff --git a/docs/docs/home.mdx b/docs/docs/home.mdx index a82ecea8..e180525e 100644 --- a/docs/docs/home.mdx +++ b/docs/docs/home.mdx @@ -99,7 +99,6 @@ This list provides an overview of the schemas available in this repository. Each | **[Location Extended](./reference/location_extended.mdx)** | This schema extension is the most detailed when it comes to location, you'll find all the layers you can think of. It defines its own hierarchical Location.Rack, incompatible with the flat one extensions/rack defines, so load one or the other, not both. | | **[Modules Linecards](./reference/modules_linecards.mdx)** | This schema extension allows you to capture Linecard related information like the version. You can insert the Linecard into a Dcim Physical Device and leverage the Linecard type model. The Linecard can accept PIC to help configure PORT information like breakout-capabilities and configurations. | | **[Modules Routing Engine](./reference/modules_routing_engine.mdx)** | This schema extension allows you to capture Routing Engine related information like the version. You can insert the Routing Engine into a Dcim Physical Device and leverage the Routing Engine type model. | -| **[Optical Transport](./reference/optical_transport.mdx)** | Comprehensive optical transport network schemas for DWDM/WDM systems (ADVA FSP 3000 and similar platforms). Covers four layers: wavelength (ITU-T G.694.1 grid, optical bands, DWDM channels), topology (logical optical nodes, passive multiplexers, fiber links), equipment (transponder/amplifier/ROADM modules, ROADM degrees, WSS cross-connects), and service (end-to-end optical services, optical paths, path segments). Not designed to be loaded together with extensions/optical_multiplexer. | | **[QoS](./reference/qos.mdx)** | This schema extension contains models for Quality of Service (QoS) | | **[Security](./reference/security.mdx)** | This schema extension contains models for implementing detailed security. | | **[Topology](./reference/topology.mdx)** | A schema for defining and managing network topology, strategies, and services. | @@ -124,7 +123,6 @@ This list provides an overview of the schemas available in this repository. Each | **[Location Minimal](./reference/location_minimal.mdx)** | This schema extension provides a self-contained Region -> Country -> Metro -> Site hierarchy for storing location data, with the Site carrying facility, physical address, timezone and status. Its Site node is the same as the one in extensions/location_site, so the two can be loaded together. A location name is unique across every tier, so a single-country deployment should enter the hierarchy at Country, with Region as the national node, rather than repeating a country under several regions. | | **[Location Site](./reference/location_site.mdx)** | This schema extension introduces a Site node with facility, physical address, timezone and status, for deployments that want a flat list of sites without a hierarchy. It is the same Site node as in extensions/location_minimal, which adds Region, Country and Metro tiers above it. | | **[MLAG](./reference/mlag.mdx)** | This schema extension contains the foundations to capture Multi-Chassis Link Aggregation Groups (MLAG). It comes on top of the LAG extension. In this implementation, a MLAG interface is essentially a LAG interface but linked to a MLAG domain (instead of a device). The MLAG domain regroups devices together (usually 2) and is built over LAG interfaces used as peer-link between the devices. MLAG interfaces defined at the MLAG domain level are then spread across all devices in the domain. This is a deliberately minimal implementation of MLAG, meant to blend with models you already have. For example, in a data center fabric you might already have a LeafGroup or similar concept: have it inherit from GenericMlagDomain and add the relationships and attributes you need. Not covered yet: the layer 3 overlay for MLAG interfaces (loopback, peer address ...) and surfacing MLAG interfaces on each device in the domain. | -| **[Optical Multiplexer](./reference/optical_multiplexer.mdx)** | This schema extension models optical add-drop multiplexers (OADM) and the wavelength division multiplexing (WDM) channels they carry, for both CWDM and DWDM. It adds an Optical Multiplexer device with front and rear interfaces, a WDM Channel node holding channel number, wavelength and frequency, and a WDM Transceiver flavour of the transceiver model tuned to a channel. Some vendors configure tunable optics by wavelength or frequency rather than channel number; the WDM Channel node gives you a single entry in Infrahub for all three. Not designed to be loaded together with experimental/optical_transport, which covers the same domain in more depth. | | **[Patch Panel](./reference/patch_panel.mdx)** | This schema extension allows you to capture patch panel related information like rear and front interfaces and the mapping between them. You can insert the patch panel into a rack and leverage the device type model. Cassettes and other inserts are tracked as regular device modules in module bays, through extensions/device_module. The front and rear interfaces accept all sorts of connectors, so you can plug cables, circuits and cross-connects into them. | | **[Physical Disk](./reference/physical_disk.mdx)** | Simple schema allowing you to capture physical disk information for inventory and lifecycle management. This extension works with any kind of device: apply the DcimDeviceWithPhysicalDisks generic to a model to enable disk tracking. You might also link disks to a location, for instance to capture spares. | | **[PSU Module](./reference/device_module_psu.mdx)** | This schema extension adds a PSU (Power Supply Unit) flavour on top of the generic Module and Module Type from extensions/device_module, so you can track power supplies installed in a device's module bays with PSU-specific attributes such as wattage and hot-swap capability. | diff --git a/docs/docs/reference/optical_multiplexer.mdx b/docs/docs/reference/optical_multiplexer.mdx deleted file mode 100644 index 88f555b8..00000000 --- a/docs/docs/reference/optical_multiplexer.mdx +++ /dev/null @@ -1,461 +0,0 @@ ---- -title: Optical Multiplexer ---- - -This schema extension models optical add-drop multiplexers (OADM) and the wavelength division multiplexing (WDM) channels they carry, for both CWDM and DWDM. It adds an Optical Multiplexer device with front and rear interfaces, a WDM Channel node holding channel number, wavelength and frequency, and a WDM Transceiver flavour of the transceiver model tuned to a channel. - -Some vendors configure tunable optics by wavelength or frequency rather than channel number; the WDM Channel node gives you a single entry in Infrahub for all three. - -Not designed to be loaded together with experimental/optical_transport, which covers the same domain in more depth. - -## Details - -- **Dependencies:** - - [base](dcim) - - [extensions/transceiver](transceiver) - -## Nodes - -### OpticalMultiplexer - -- **Label:** Optical Multiplexer -- **Description:** An OADM (Optical Add Drop Multiplexer) supporting various WDM (Wavelength Division Multiplexing) technologies. -- **Namespace:** Dcim -- **Icon:** mdi:transit-connection-variant -- **Human Friendly ID:** name__value -- **Inherit From:** DcimPhysicalDevice - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| name | | Text | False | | | -| wdm_type | Type of WDM technology (e.g. CWDM, DWDM) | Dropdown | False | dwdm | cwdm, dwdm | -| description | | Text | True | | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| front_interfaces | DcimOadmFrontInterface | True | many | Component | -| rear_interface | DcimOadmRearInterface | True | one | Component | - -### OadmFrontInterface - -- **Label:** Optical Multiplexer Front Interfaces -- **Description:** Client-side interface of an optical add-drop multiplexer, carrying a single channel. -- **Namespace:** Dcim -- **Uniqueness Constraints:** - - optical_multiplexer, name__value -- **Human Friendly ID:** optical_multiplexer__name__value, name__value -- **Inherit From:** DcimEndpoint, DcimGenericOadmInterface - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| optical_multiplexer | DcimOpticalMultiplexer | False | one | Parent | -| channels | DcimWdmChannel | True | many | Attribute | - -### OadmRearInterface - -- **Label:** Optical Multiplexer Rear Interfaces -- **Description:** Line-side interface of an optical add-drop multiplexer, carrying the multiplexed signal. -- **Namespace:** Dcim -- **Uniqueness Constraints:** - - optical_multiplexer, name__value -- **Human Friendly ID:** optical_multiplexer__name__value, name__value -- **Inherit From:** DcimEndpoint, DcimGenericOadmInterface - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| optical_multiplexer | DcimOpticalMultiplexer | False | one | Parent | - -### WdmChannel - -- **Label:** WDM Channel -- **Description:** A WDM channel with its wavelength and frequency. -- **Namespace:** Dcim -- **Icon:** game-icons:laser-warning -- **Uniqueness Constraints:** - - frequency__value, wavelength__value, channel__value, wdm_type__value - - channel__value, wdm_type__value -- **Human Friendly ID:** wdm_type__value, channel__value - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| channel | WDM channel number. | Number | False | | | -| wdm_type | Type of WDM technology (e.g. CWDM, DWDM) | Dropdown | False | dwdm | cwdm, dwdm | -| wavelength | Wavelength of the channel in nm. | Text | False | | | -| frequency | Frequency of the channel in GHz. | Text | False | | | - -### WdmTransceiver - -- **Label:** WDM Transceiver -- **Description:** Transceiver tuned to a Wavelength Division Multiplexing channel. -- **Namespace:** Dcim -- **Icon:** mdi:laser-pointer -- **Inherit From:** DcimGenericTransceiver - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| wdm_type | Type of WDM technology (e.g. CWDM, DWDM) | Dropdown | False | dwdm | cwdm, dwdm | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| wdm_channel | DcimWdmChannel | False | one | Attribute | - -## Generics - -### GenericOadmInterface - -- **Label:** Optical Multiplexer Interfaces -- **Description:** Generic interface of an optical add-drop multiplexer, front or rear. -- **Namespace:** Dcim -- **Icon:** mdi:ethernet - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| name | | Text | False | | | -| description | | Text | True | | | -| connector_type | | Dropdown | False | | fc, lc, lc_pc, lc_upc, lc_apc, lsh, lsh_pc, lsh_upc, lsh_apc, lx_5, lx_5_pc, lx_5_upc, lx_5_apc, mpo, mtrj, sc, sc_pc, sc_upc, sc_apc, st, cs, sn, sma_905, sma_906, urm_p2, urm_p4, urm_p8, splice | - -## Code - -```yaml -version: '1.0' -generics: -- name: GenericOadmInterface - namespace: Dcim - description: Generic interface of an optical add-drop multiplexer, front or rear. - label: Optical Multiplexer Interfaces - icon: mdi:ethernet - include_in_menu: true - menu_placement: DcimOpticalMultiplexer - attributes: - - name: name - kind: Text - optional: false - order_weight: 1000 - - name: description - kind: Text - optional: true - order_weight: 1100 - - name: connector_type - kind: Dropdown - choices: - - name: fc - label: FC - description: Standardized fiber optic connector used primarily in datacom and - telecom applications. - - name: lc - label: LC - description: Compact fiber optic connector with a push-pull mechanism. - - name: lc_pc - label: LC/PC - description: Polished LC connector providing physical contact (PC). - - name: lc_upc - label: LC/UPC - description: Ultra-Physical Contact (UPC) variant of the LC connector with enhanced - polish. - - name: lc_apc - label: LC/APC - description: Angled Physical Contact (APC) version of the LC connector with - a slanted fiber end-face. - - name: lsh - label: LSH - description: European fiber optic connector offering high durability. - - name: lsh_pc - label: LSH/PC - description: Physical Contact version of LSH with standard polish. - - name: lsh_upc - label: LSH/UPC - description: Ultra-Physical Contact variant of LSH, minimizing return loss with - a superior polish. - - name: lsh_apc - label: LSH/APC - description: Angled Physical Contact version of LSH, designed to reduce back - reflections. - - name: lx_5 - label: LX.5 - description: Miniaturized fiber optic connector similar to LC but with an additional - shutter mechanism. - - name: lx_5_pc - label: LX.5/PC - description: Physical Contact version of LX.5. - - name: lx_5_upc - label: LX.5/UPC - description: Ultra-Physical Contact variant of LX.5. - - name: lx_5_apc - label: LX.5/APC - description: Angled Physical Contact version of LX.5. - - name: mpo - label: MPO - description: Multi-fiber Push-On connector typically used in data centers for - high-speed applications. - - name: mtrj - label: MTRJ - description: Male-to-female fiber optic connector with two fibers. - - name: sc - label: SC - description: Square fiber optic connector with push-pull lock. - - name: sc_pc - label: SC/PC - description: Physical Contact SC connector with a polished end-face. - - name: sc_upc - label: SC/UPC - description: Ultra-Physical Contact variant of SC. - - name: sc_apc - label: SC/APC - description: Angled Physical Contact version of SC. - - name: st - label: ST - description: Bayonet-style fiber optic connector primarily used in industrial - and military applications. - - name: cs - label: CS - description: Compact connector with a high-density duplex configuration. - - name: sn - label: SN - description: Small-form connector with dual fibers. - - name: sma_905 - label: SMA 905 - description: Stainless steel fiber optic connector. - - name: sma_906 - label: SMA 906 - description: Variant of SMA 905 with similar durability, frequently used in - high-vibration settings. - - name: urm_p2 - label: URM-P2 - description: Specialized fiber optic connector for industrial and harsh environments. - - name: urm_p4 - label: URM-P4 - description: Similar to URM-P2 but designed for higher performance. - - name: urm_p8 - label: URM-P8 - description: Enhanced version of URM connectors with higher protection. - - name: splice - label: Splice - description: Permanent fiber connection method where two fiber ends are fused. - optional: false - order_weight: 1200 -nodes: -- name: OpticalMultiplexer - namespace: Dcim - description: An OADM (Optical Add Drop Multiplexer) supporting various WDM (Wavelength - Division Multiplexing) technologies. - label: Optical Multiplexer - icon: mdi:transit-connection-variant - include_in_menu: true - inherit_from: - - DcimPhysicalDevice - human_friendly_id: - - name__value - order_by: - - name__value - display_label: name__value - attributes: - - name: name - kind: Text - unique: true - optional: false - order_weight: 1000 - - name: wdm_type - kind: Dropdown - default_value: dwdm - choices: - - name: cwdm - label: CWDM (Coarse Wavelength Division Multiplexing) - description: Supports multiple wavelengths for communication up to 70km. - color: '#0099cc' - - name: dwdm - label: DWDM (Dense Wavelength Division Multiplexing) - description: Supports dense wavelengths and amplification for long-distance - communication. - color: '#9933cc' - optional: false - description: Type of WDM technology (e.g. CWDM, DWDM) - order_weight: 1300 - - name: description - kind: Text - optional: true - order_weight: 1100 - relationships: - - name: front_interfaces - peer: DcimOadmFrontInterface - kind: Component - cardinality: many - optional: true - identifier: optical_multiplexer__front_interfaces - order_weight: 1350 - - name: rear_interface - peer: DcimOadmRearInterface - kind: Component - cardinality: one - optional: true - identifier: optical_multiplexer__rear_interface - order_weight: 1450 -- name: OadmFrontInterface - namespace: Dcim - description: Client-side interface of an optical add-drop multiplexer, carrying - a single channel. - label: Optical Multiplexer Front Interfaces - include_in_menu: true - menu_placement: DcimGenericOadmInterface - inherit_from: - - DcimEndpoint - - DcimGenericOadmInterface - human_friendly_id: - - optical_multiplexer__name__value - - name__value - order_by: - - optical_multiplexer__name__value - - name__value - display_label: '{{ optical_multiplexer__name__value }} > {{ name__value }}' - uniqueness_constraints: - - - optical_multiplexer - - name__value - relationships: - - name: optical_multiplexer - peer: DcimOpticalMultiplexer - kind: Parent - cardinality: one - optional: false - identifier: optical_multiplexer__front_interfaces - order_weight: 900 - - name: channels - peer: DcimWdmChannel - kind: Attribute - cardinality: many - optional: true - identifier: oadm_interface__channels - order_weight: 1300 -- name: OadmRearInterface - namespace: Dcim - description: Line-side interface of an optical add-drop multiplexer, carrying the - multiplexed signal. - label: Optical Multiplexer Rear Interfaces - include_in_menu: true - menu_placement: DcimGenericOadmInterface - inherit_from: - - DcimEndpoint - - DcimGenericOadmInterface - human_friendly_id: - - optical_multiplexer__name__value - - name__value - order_by: - - optical_multiplexer__name__value - - name__value - display_label: '{{ optical_multiplexer__name__value }} > {{ name__value }}' - uniqueness_constraints: - - - optical_multiplexer - - name__value - relationships: - - name: optical_multiplexer - peer: DcimOpticalMultiplexer - kind: Parent - cardinality: one - optional: false - identifier: optical_multiplexer__rear_interface - order_weight: 900 -- name: WdmChannel - namespace: Dcim - description: A WDM channel with its wavelength and frequency. - label: WDM Channel - icon: game-icons:laser-warning - include_in_menu: true - human_friendly_id: - - wdm_type__value - - channel__value - order_by: - - wdm_type__value - - channel__value - display_label: '{{ wdm_type__value }} Channel {{ channel__value }}' - uniqueness_constraints: - - - frequency__value - - wavelength__value - - channel__value - - wdm_type__value - - - channel__value - - wdm_type__value - attributes: - - name: channel - kind: Number - optional: false - description: WDM channel number. - order_weight: 1000 - - name: wdm_type - kind: Dropdown - default_value: dwdm - choices: - - name: cwdm - label: CWDM (Coarse Wavelength Division Multiplexing) - description: Supports multiple wavelengths for communication up to 70km. - color: '#0099cc' - - name: dwdm - label: DWDM (Dense Wavelength Division Multiplexing) - description: Supports dense wavelengths and amplification for long-distance - communication. - color: '#9933cc' - optional: false - description: Type of WDM technology (e.g. CWDM, DWDM) - order_weight: 1100 - - name: wavelength - kind: Text - label: Wavelength (nm) - optional: false - description: Wavelength of the channel in nm. - order_weight: 1200 - - name: frequency - kind: Text - label: Frequency (GHz) - optional: false - description: Frequency of the channel in GHz. - order_weight: 1300 -- name: WdmTransceiver - namespace: Dcim - description: Transceiver tuned to a Wavelength Division Multiplexing channel. - label: WDM Transceiver - icon: mdi:laser-pointer - include_in_menu: true - menu_placement: DcimGenericTransceiver - inherit_from: - - DcimGenericTransceiver - attributes: - - name: wdm_type - kind: Dropdown - default_value: dwdm - choices: - - name: cwdm - label: CWDM (Coarse Wavelength Division Multiplexing) - description: Supports multiple wavelengths for communication up to 70km. - color: '#0099cc' - - name: dwdm - label: DWDM (Dense Wavelength Division Multiplexing) - description: Supports dense wavelengths and amplification for long-distance - communication. - color: '#9933cc' - optional: false - description: Type of WDM technology (e.g. CWDM, DWDM) - order_weight: 1150 - relationships: - - name: wdm_channel - peer: DcimWdmChannel - label: WDM Channel - kind: Attribute - cardinality: one - optional: false - identifier: wdm_transceiver__channel - order_weight: 1250 - -``` \ No newline at end of file diff --git a/docs/docs/reference/optical_transport.mdx b/docs/docs/reference/optical_transport.mdx deleted file mode 100644 index 57964bb9..00000000 --- a/docs/docs/reference/optical_transport.mdx +++ /dev/null @@ -1,1953 +0,0 @@ ---- -title: Optical Transport ---- - -Comprehensive optical transport network schemas for DWDM/WDM systems (ADVA FSP 3000 and similar platforms). Covers four layers: wavelength (ITU-T G.694.1 grid, optical bands, DWDM channels), topology (logical optical nodes, passive multiplexers, fiber links), equipment (transponder/amplifier/ROADM modules, ROADM degrees, WSS cross-connects), and service (end-to-end optical services, optical paths, path segments). Not designed to be loaded together with extensions/optical_multiplexer. - -## Details - -- **Dependencies:** - - [base](dcim) - - [extensions/device_module](device_module) - - [extensions/cable](cable) - -## Nodes - -### ITUGrid - -- **Label:** ITU-T DWDM Grid -- **Description:** ITU-T G.694.1 frequency grid standards for DWDM -- **Namespace:** Dcim -- **Icon:** mdi:grid -- **Uniqueness Constraints:** - - name__value -- **Human Friendly ID:** name__value - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| name | Grid name (e.g., 50GHz, 100GHz) | Text | False | | | -| channel_spacing_ghz | Channel spacing in GHz (50, 100, etc.) | Number | False | | | -| reference_frequency_thz | Reference frequency in THz (e.g., '193.1' for 1550nm) | Text | False | | | -| description | Grid description and use cases | Text | True | | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| bands | DcimOpticalBand | True | many | Component | - -### OpticalBand - -- **Label:** Optical Band -- **Description:** Optical frequency band (C-Band, L-Band, S-Band) -- **Namespace:** Dcim -- **Icon:** mdi:sine-wave -- **Uniqueness Constraints:** - - grid, band_name__value -- **Human Friendly ID:** grid__name__value, band_name__value - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| band_name | Optical band designation | Dropdown | False | | c_band, l_band, s_band, o_band | -| start_frequency_thz | Band start frequency in THz (e.g., '191.0') | Text | False | | | -| end_frequency_thz | Band end frequency in THz (e.g., '196.1') | Text | False | | | -| start_wavelength_nm | Band start wavelength in nm (e.g., '1530.0') | Text | True | | | -| end_wavelength_nm | Band end wavelength in nm (e.g., '1565.0') | Text | True | | | -| computed_name | Auto-generated band identifier (e.g., 50GHz-C_BAND) | Text | False | | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| grid | DcimITUGrid | False | one | Parent | -| channels | DcimDWDMChannel | True | many | Component | - -### DWDMChannel - -- **Label:** DWDM Channel -- **Description:** Individual DWDM wavelength/channel (CH20, CH21, etc.) -- **Namespace:** Dcim -- **Icon:** mdi:wave -- **Uniqueness Constraints:** - - band, channel_number__value - - channel_name__value - - channel_number__value, frequency_thz__value, wavelength_nm__value -- **Human Friendly ID:** channel_name__value - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| channel_name | ITU DWDM Channel identifier (computed as CH + channel number, e.g., CH20, CH21, CH40) | Text | False | | | -| adva_channel_name | ADVA-specific channel name (e.g., D02, DC1, D32) | Text | True | | | -| channel_number | ITU channel number (e.g., 20, 21, 60) | Number | False | | | -| frequency_thz | Center frequency in THz (e.g., '192.1') | Text | False | | | -| wavelength_nm | Center wavelength in nm (e.g., '1560.61') | Text | False | | | -| description | Channel notes and usage | Text | True | | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| band | DcimOpticalBand | False | one | Parent | -| mux_assignments | DcimMuxChannelAssignment | True | many | Component | -| mappings | DcimChannelMapping | True | many | Generic | - -### MuxChannelAssignment - -- **Label:** Mux Channel Assignment -- **Description:** Maps DWDM channel to multiplexer port (equipment configuration) -- **Namespace:** Dcim -- **Icon:** mdi:cable-data -- **Uniqueness Constraints:** - - optical_device, port_number__value - - optical_device, channel -- **Human Friendly ID:** optical_device__computed_name__value, port_number__value - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| port_number | Physical port number on multiplexer (e.g., 1-96) | Number | False | | | -| tx_power_dbm | Configured transmit power in dBm (e.g., '0.0') | Text | True | | | -| status | Channel assignment status | Dropdown | False | configured | configured, active, disabled, failed | -| computed_name | Auto-generated assignment identifier | Text | False | | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| channel | DcimDWDMChannel | False | one | Attribute | -| optical_device | DcimOpticalDevice | False | one | Parent | - -### FiberMapping - -- **Label:** Fiber Mapping -- **Description:** Long-haul fiber link between degrees on DIFFERENT ROADMs/sites (kilometers distance) -- **Namespace:** Dcim -- **Icon:** mdi:fiber-optic -- **Uniqueness Constraints:** - - fiber_link, channel -- **Human Friendly ID:** fiber_link__link_id__value, channel__channel_name__value -- **Inherit From:** DcimChannelMapping - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| allocation_status | Allocation status | Dropdown | False | reserved | reserved, allocated, in_use, maintenance, failed | -| tx_power_dbm | Measured transmit power in dBm (e.g., '0.5') | Text | True | | | -| rx_power_dbm | Measured receive power in dBm (e.g., '-2.3') | Text | True | | | -| osnr_db | Optical Signal-to-Noise Ratio in dB (e.g., '25.5') | Text | True | | | -| allocated_date | Date channel was allocated | DateTime | True | | | -| computed_name | Auto-generated mapping identifier | Text | False | | | -| description | Additional fiber mapping details | Text | True | | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| fiber_link | DcimFiberLink | False | one | Attribute | - -### OpticalNode - -- **Label:** Optical Node -- **Description:** Logical network endpoint in optical topology (graph node, not physical device) -- **Namespace:** Dcim -- **Icon:** mdi:lan -- **Uniqueness Constraints:** - - name__value -- **Human Friendly ID:** name__value -- **Inherit From:** DcimOpticalDevice - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| computed_name | Computed name for display | Text | False | | | -| name | Logical node name (e.g., GENESIS-OL-10, SKYLINE-OL-1) | Text | False | | | -| node_type | Topology role of this node | Dropdown | False | | endpoint, intermediate, branching | -| description | Node notes and function | Text | True | | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| device | DcimPhysicalDevice | True | one | Attribute | -| transponder_modules | DcimTransponderModule | True | many | Attribute | -| links | DcimFiberLink | True | many | Component | - -### PassiveMultiplexer - -- **Label:** Passive Multiplexer -- **Description:** Passive CWDM/DWDM multiplexer (fixed port count, no modules) -- **Namespace:** Dcim -- **Icon:** mdi:resistor-nodes -- **Uniqueness Constraints:** - - name__value -- **Human Friendly ID:** name__value -- **Inherit From:** DcimPhysicalDevice - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| computed_name | Computed name for display | Text | False | | | -| name | Passive mux name (e.g., CHI-PMUX-01) | Text | False | | | -| port_count | Number of fixed ports (e.g., 8, 16, 32, 40) | Number | False | | | -| mux_type | Type of passive multiplexer | Dropdown | False | | cwdm, dwdm | -| description | Device notes | Text | True | | | - -### FiberLink - -- **Label:** Fiber Link -- **Description:** Fiber connecting two optical nodes (graph edge) -- **Namespace:** Dcim -- **Icon:** mdi:cable-data -- **Uniqueness Constraints:** - - link_id__value -- **Human Friendly ID:** link_id__value - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| link_id | Unique link identifier (e.g., LINK-001) | Text | False | | | -| distance_km | Physical distance in kilometers (e.g., '920.5') | Text | True | | | -| attenuation_db | Total fiber attenuation in dB (e.g., '18.4') | Text | True | | | -| status | Operational status of fiber link | Dropdown | False | planned | planned, active, standby, maintenance, failed | -| commissioned_date | Date link was commissioned | DateTime | True | | | -| description | Additional link details | Text | True | | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| endpoints | DcimOpticalNode | False | many | Attribute | -| mappings | DcimFiberMapping | True | many | Component | - -### OpticalModuleType - -- **Label:** Optical Module Type -- **Description:** Categorization of optical modules by function -- **Namespace:** Dcim -- **Icon:** mdi:expansion-card -- **Inherit From:** DcimGenericModuleType - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| name | Module type name | Text | False | | | -| description | Module type description | Text | True | | | -| module_category | Functional category of the module | Dropdown | False | | transponder, multiplexer, amplifier, monitoring, control, power, cooling | - -### TransponderModule - -- **Label:** Transponder Module -- **Description:** Optical-electrical-optical conversion module (100G, 400G coherent) installed in chassis -- **Namespace:** Dcim -- **Icon:** mdi:chip -- **Uniqueness Constraints:** - - computed_name__value -- **Human Friendly ID:** computed_name__value -- **Inherit From:** DcimGenericModule - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| computed_name | Computed module name (e.g., dc1-leaf1 > slot 1-TPD-1/1) | Text | False | | | -| slot_number | Slot number in chassis (e.g., '1/1', '1/2', '2/1') | Text | False | | | -| capacity_gbps | Line rate capacity in Gbps (e.g., 100, 200, 400) | Number | False | | | -| modulation_format | Modulation format for optical transmission | Dropdown | False | | dp_qpsk, dp_16qam, dp_8qam, dp_64qam | -| tunable_range | Wavelength tuning capability | Dropdown | False | | c_band, c_l_band, fixed | -| fec_type | Forward error correction type | Dropdown | True | | sd_fec, hd_fec, c_fec, o_fec | -| client_interface | Client-side interface (e.g., 100GE, 400GE, OTU4) | Text | True | | | -| line_interface | Line-side interface (e.g., CFP2-DCO, QSFP28, QSFP-DD) | Text | True | | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| tuned_channel | DcimDWDMChannel | True | one | Attribute | -| connected_to_optical_node | DcimOpticalNode | True | one | Attribute | - -### OpticalMultiplexerModule - -- **Label:** Optical Multiplexer Module -- **Description:** Multiplexer/Demultiplexer/ROADM module for wavelength management installed in chassis -- **Namespace:** Dcim -- **Icon:** mdi:router-network -- **Human Friendly ID:** computed_name__value -- **Inherit From:** DcimGenericModule - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| computed_name | Computed module name (e.g., dc1-leaf1 > slot 1-MUX-1/3) | Text | False | | | -| slot_number | Slot number in chassis (e.g., '1/3', '1/4', '2/3') | Text | False | | | -| mux_type | Type of multiplexer technology | Dropdown | False | | passive_mux, passive_demux, oadm, roadm, wss | -| channel_capacity | Number of supported DWDM channels (e.g., 40, 80, 96) | Number | False | | | -| technology | Underlying multiplexer technology | Dropdown | True | | thin_film_filter, awg, wss_lcos, wss_mems | -| degree_count | Number of ROADM degrees (e.g., 2, 4, 8, 16) | Number | True | | | -| colorless | Colorless add/drop capability (any channel on any port) | Boolean | True | False | | -| directionless | Directionless add/drop capability (any port to any degree) | Boolean | True | False | | -| contentionless | Contentionless add/drop (CDC - Colorless, Directionless, Contentionless) | Boolean | True | False | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| degrees | DcimRoadmDegree | True | many | Component | -| mappings | DcimChannelMapping | True | many | Attribute | - -### RoadmDegree - -- **Label:** ROADM Degree -- **Description:** ROADM degree with line port (OL-1, OL-2, etc.) -- **Namespace:** Dcim -- **Icon:** mdi:lan-connect -- **Uniqueness Constraints:** - - roadm, degree_number__value - - roadm, line_port__value -- **Human Friendly ID:** computed_name__value - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| computed_name | Computed name (e.g., ROADM-SITE-A-OL-1) | Text | False | | | -| degree_number | Degree number (1-based, e.g., 1, 2, 3, 4) | Number | False | | | -| line_port | Line port designation (e.g., OL-1, OL-2, OL-7) | Text | False | | | -| direction | Directional designation of degree | Dropdown | False | | north, south, east, west, express, local | -| degree_type | Type of ROADM degree | Dropdown | False | | line, express, add_drop | -| wavelength_capacity | Number of wavelengths supported on this degree | Number | True | | | -| description | Additional degree details | Text | True | | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| roadm | DcimOpticalMultiplexerModule | False | one | Parent | -| connected_fiber | DcimFiberLink | True | one | Attribute | -| channel_mappings | DcimChannelMapping | True | many | Attribute | - -### WSSConnect - -- **Label:** WSS Connect -- **Description:** Internal WSS cross-connect between degrees on SAME ROADM device (0m distance) -- **Namespace:** Dcim -- **Icon:** mdi:swap-horizontal -- **Uniqueness Constraints:** - - roadm, channel -- **Human Friendly ID:** computed_name__value -- **Inherit From:** DcimChannelMapping - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| computed_name | Computed connection ID | Text | False | | | -| description | Additional cross-connect details | Text | True | | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| roadm | DcimOpticalMultiplexerModule | False | one | Attribute | - -### CableMapping - -- **Label:** Cable Mapping -- **Description:** Local patch cable between degrees on DIFFERENT ROADMs (meters distance, same site) -- **Namespace:** Dcim -- **Icon:** mdi:cable-data -- **Uniqueness Constraints:** - - cable, channel -- **Human Friendly ID:** computed_name__value -- **Inherit From:** DcimChannelMapping - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| computed_name | Computed mapping name (e.g., CABLE-123-CH58) | Text | False | | | -| description | Additional cable mapping details | Text | True | | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| cable | DcimCable | False | one | Attribute | - -### OpticalAmplifierModule - -- **Label:** Optical Amplifier Module -- **Description:** Optical signal amplifier module (EDFA, Raman, VGC) installed in chassis -- **Namespace:** Dcim -- **Icon:** mdi:amplifier -- **Human Friendly ID:** computed_name__value -- **Inherit From:** DcimGenericModule - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| computed_name | Computed module name (e.g., dc1-leaf1 > slot 1-AMP-1/5) | Text | False | | | -| slot_number | Slot number in chassis (e.g., '1/5', '1/6', '2/5') | Text | False | | | -| amplifier_type | Type of optical amplifier | Dropdown | False | | edfa, raman, soa, vgc, hybrid | -| stage | Amplifier stage position | Dropdown | False | | booster, inline, preamplifier | -| gain_db | Typical gain in dB (e.g., '20.0', '17.5') | Text | True | | | -| max_output_power_dbm | Maximum output power in dBm (e.g., '17.0', '23.0') | Text | True | | | -| noise_figure_db | Noise figure in dB (e.g., '5.5', '4.8') | Text | True | | | -| operating_band | Operating wavelength band | Dropdown | False | | c_band, l_band, c_l_band | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| amplified_link | DcimFiberLink | True | one | Attribute | - -### OpticalService - -- **Label:** Optical Service -- **Description:** End-to-end optical transport service (customer circuit) spanning multiple sites -- **Namespace:** Dcim -- **Icon:** mdi:transit-connection-variant -- **Uniqueness Constraints:** - - service_id__value -- **Human Friendly ID:** service_id__value - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| service_id | Unique service identifier | Text | False | | | -| service_name | Customer service name (e.g., 'ATC 10G - Albion to Arco #2') | Text | False | | | -| service_type | Type of optical service | Dropdown | False | | wavelength, transport, ip_transit | -| bandwidth | Service bandwidth (e.g., '10G', '100G', '400G') | Text | False | | | -| status | Service operational status | Dropdown | False | planned | planned, provisioned, active, maintenance, failed | -| customer_circuit_id | Customer's circuit ID (e.g., '99/OKFS/000029//SYG') | Text | True | | | -| provision_date | Date service was provisioned | DateTime | True | | | -| description | Additional service details | Text | True | | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| transponders | DcimTransponderModule | False | many | Attribute | -| channel | DcimDWDMChannel | False | one | Attribute | -| primary_path | DcimOpticalPath | False | one | Component | -| backup_path | DcimOpticalPath | True | one | Component | - -### OpticalPath - -- **Label:** Optical Path -- **Description:** Ordered sequence of segments forming an optical path through the network -- **Namespace:** Dcim -- **Icon:** mdi:map-marker-path -- **Human Friendly ID:** name__value - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| name | Path name (e.g., MS-W17D5-primary) | Text | False | | | -| path_type | Type of path (primary, backup, express) | Dropdown | False | | primary, backup, express | -| total_distance_km | Total path distance in kilometers | Text | True | | | -| total_loss_db | Total optical loss in dB | Text | True | | | -| hop_count | Number of ROADM hops in path | Number | True | | | -| is_active | Whether this path is currently active | Boolean | False | True | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| segments | DcimPathSegment | True | many | Component | -| is_primary_of_service | DcimOpticalService | True | one | Attribute | -| is_backup_of_service | DcimOpticalService | True | one | Attribute | - -### PathSegment - -- **Label:** Path Segment -- **Description:** One hop in optical path - references channel mapping (fiber, cable, or cross-connect) -- **Namespace:** Dcim -- **Icon:** mdi:ray-start-arrow -- **Uniqueness Constraints:** - - path, segment_order__value -- **Human Friendly ID:** computed_name__value - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| computed_name | Computed segment name (e.g., MS-W17D5-primary-SEG1) | Text | False | | | -| segment_order | Order of this segment in the path (1, 2, 3...) | Number | False | | | -| segment_type | Type of segment | Dropdown | False | | fiber, cross_connect, cable, add, drop | -| loss_db | Optical loss for this segment in dB | Text | True | | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| path | DcimOpticalPath | False | one | Parent | -| channel_mapping | DcimChannelMapping | True | one | Attribute | - -## Generics - -### ChannelMapping - -- **Label:** Channel Mapping -- **Description:** Generic base for any channel allocation/routing (fiber, cable, or internal cross-connect) -- **Namespace:** Dcim - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| is_active | Whether this mapping is currently active | Boolean | False | True | | -| configured_date | Date this mapping was configured | DateTime | True | | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| segment | DcimPathSegment | False | one | Parent | -| channel | DcimDWDMChannel | False | one | Attribute | -| degrees | DcimRoadmDegree | False | many | Attribute | - -### OpticalDevice - -- **Label:** Optical Multiplexer Device -- **Description:** Generic interface for any device that can multiplex DWDM channels (active or passive) -- **Namespace:** Dcim -- **Uniqueness Constraints:** - - computed_name__value - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| computed_name | | Text | False | | | - -## Extensions - -:::note - -In this context "extensions" refer to modifications or additions to the existing schema, such as adding new attributes, relationships, or other schema elements. - -::: - -### DcimPhysicalDevice - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| optical_node | DcimOpticalNode | True | many | Generic | - -## Code - -```yaml -version: '1.0' -generics: -- name: ChannelMapping - namespace: Dcim - description: Generic base for any channel allocation/routing (fiber, cable, or internal - cross-connect) - label: Channel Mapping - include_in_menu: false - attributes: - - name: is_active - kind: Boolean - optional: false - default_value: true - description: Whether this mapping is currently active - - name: configured_date - kind: DateTime - optional: true - description: Date this mapping was configured - relationships: - - name: segment - peer: DcimPathSegment - cardinality: one - kind: Parent - optional: false - identifier: channel_mapping__path_segments - description: Optical Path segment associated with this mapping - - name: channel - peer: DcimDWDMChannel - cardinality: one - kind: Attribute - optional: false - identifier: dwdm_channel__mappings - description: DWDM channel being mapped/routed - - name: degrees - peer: DcimRoadmDegree - cardinality: many - max_count: 2 - kind: Attribute - optional: false - identifier: roadm_degree__channel_mappings - description: Two ROADM degrees involved in this mapping (bidirectional) -- name: OpticalDevice - namespace: Dcim - description: Generic interface for any device that can multiplex DWDM channels (active - or passive) - label: Optical Multiplexer Device - include_in_menu: false - uniqueness_constraints: - - - computed_name__value - attributes: - - name: computed_name - kind: Text - optional: false -nodes: -- name: ITUGrid - namespace: Dcim - include_in_menu: true - label: ITU-T DWDM Grid - description: ITU-T G.694.1 frequency grid standards for DWDM - icon: mdi:grid - display_label: name__value - human_friendly_id: - - name__value - uniqueness_constraints: - - - name__value - attributes: - - name: name - kind: Text - optional: false - description: Grid name (e.g., 50GHz, 100GHz) - - name: channel_spacing_ghz - kind: Number - optional: false - description: Channel spacing in GHz (50, 100, etc.) - - name: reference_frequency_thz - kind: Text - optional: false - description: Reference frequency in THz (e.g., '193.1' for 1550nm) - - name: description - kind: Text - optional: true - description: Grid description and use cases - relationships: - - name: bands - peer: DcimOpticalBand - cardinality: many - kind: Component - optional: true - description: Optical bands using this grid -- name: OpticalBand - namespace: Dcim - include_in_menu: true - label: Optical Band - description: Optical frequency band (C-Band, L-Band, S-Band) - icon: mdi:sine-wave - display_label: computed_name__value - human_friendly_id: - - grid__name__value - - band_name__value - uniqueness_constraints: - - - grid - - band_name__value - attributes: - - name: band_name - kind: Dropdown - optional: false - choices: - - name: c_band - label: C-Band - description: Conventional Band (1530-1565 nm) - color: '#ff6b6b' - - name: l_band - label: L-Band - description: Long Band (1565-1625 nm) - color: '#4ecdc4' - - name: s_band - label: S-Band - description: Short Band (1460-1530 nm) - color: '#95e1d3' - - name: o_band - label: O-Band - description: Original Band (1260-1360 nm) - color: '#f38181' - description: Optical band designation - - name: start_frequency_thz - kind: Text - optional: false - description: Band start frequency in THz (e.g., '191.0') - - name: end_frequency_thz - kind: Text - optional: false - description: Band end frequency in THz (e.g., '196.1') - - name: start_wavelength_nm - kind: Text - optional: true - description: Band start wavelength in nm (e.g., '1530.0') - - name: end_wavelength_nm - kind: Text - optional: true - description: Band end wavelength in nm (e.g., '1565.0') - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: '{{ grid__name__value }}-{{ band_name__value|upper }}' - description: Auto-generated band identifier (e.g., 50GHz-C_BAND) - relationships: - - name: grid - peer: DcimITUGrid - cardinality: one - kind: Parent - optional: false - description: Parent ITU grid - - name: channels - peer: DcimDWDMChannel - cardinality: many - kind: Component - optional: true - description: DWDM channels in this band -- name: DWDMChannel - namespace: Dcim - include_in_menu: true - label: DWDM Channel - description: Individual DWDM wavelength/channel (CH20, CH21, etc.) - icon: mdi:wave - display_label: channel_name__value - human_friendly_id: - - channel_name__value - order_by: - - band__computed_name__value - - channel_number__value - uniqueness_constraints: - - - band - - channel_number__value - - - channel_name__value - - - channel_number__value - - frequency_thz__value - - wavelength_nm__value - attributes: - - name: channel_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: CH{{ channel_number__value }} - description: ITU DWDM Channel identifier (computed as CH + channel number, e.g., - CH20, CH21, CH40) - - name: adva_channel_name - kind: Text - optional: true - description: ADVA-specific channel name (e.g., D02, DC1, D32) - - name: channel_number - kind: Number - optional: false - description: ITU channel number (e.g., 20, 21, 60) - - name: frequency_thz - kind: Text - optional: false - description: Center frequency in THz (e.g., '192.1') - - name: wavelength_nm - kind: Text - optional: false - description: Center wavelength in nm (e.g., '1560.61') - - name: description - kind: Text - optional: true - description: Channel notes and usage - relationships: - - name: band - peer: DcimOpticalBand - cardinality: one - kind: Parent - optional: false - description: Parent optical band - - name: mux_assignments - peer: DcimMuxChannelAssignment - cardinality: many - kind: Component - optional: true - description: Mux port assignments for this channel - - name: mappings - peer: DcimChannelMapping - cardinality: many - kind: Generic - optional: true - description: Fiber allocations for this channel -- name: MuxChannelAssignment - namespace: Dcim - include_in_menu: true - label: Mux Channel Assignment - description: Maps DWDM channel to multiplexer port (equipment configuration) - icon: mdi:cable-data - display_label: computed_name__value - human_friendly_id: - - optical_device__computed_name__value - - port_number__value - uniqueness_constraints: - - - optical_device - - port_number__value - - - optical_device - - channel - attributes: - - name: port_number - kind: Number - optional: false - description: Physical port number on multiplexer (e.g., 1-96) - - name: tx_power_dbm - kind: Text - optional: true - description: Configured transmit power in dBm (e.g., '0.0') - - name: status - kind: Dropdown - optional: false - default_value: configured - choices: - - name: configured - label: Configured - description: Channel is configured on port - color: '#f5a623' - - name: active - label: Active - description: Channel is actively transmitting - color: '#00cc00' - - name: disabled - label: Disabled - description: Channel is disabled - color: '#cccccc' - - name: failed - label: Failed - description: Channel has failed - color: '#cc0000' - description: Channel assignment status - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: '{{ optical_device__computed_name__value }}-P{{ ''{:02d}''.format(port_number__value) - }}-{{ channel__channel_name__value }}' - description: Auto-generated assignment identifier - relationships: - - name: channel - peer: DcimDWDMChannel - cardinality: one - kind: Attribute - optional: false - description: DWDM channel assigned to this port - - name: optical_device - peer: DcimOpticalDevice - cardinality: one - kind: Parent - optional: false - identifier: mux_channel_assignments - description: Multiplexer device (OpticalNode or PassiveMultiplexer) -- name: FiberMapping - namespace: Dcim - inherit_from: - - DcimChannelMapping - include_in_menu: true - label: Fiber Mapping - description: Long-haul fiber link between degrees on DIFFERENT ROADMs/sites (kilometers - distance) - icon: mdi:fiber-optic - display_label: computed_name__value - human_friendly_id: - - fiber_link__link_id__value - - channel__channel_name__value - uniqueness_constraints: - - - fiber_link - - channel - attributes: - - name: allocation_status - kind: Dropdown - optional: false - default_value: reserved - choices: - - name: reserved - label: Reserved - description: Channel reserved but not in use - color: '#f5a623' - - name: allocated - label: Allocated - description: Channel allocated to service - color: '#0099cc' - - name: in_use - label: In Use - description: Channel actively carrying traffic - color: '#00cc00' - - name: maintenance - label: Maintenance - description: Channel under maintenance - color: '#ff9800' - - name: failed - label: Failed - description: Channel has failed - color: '#cc0000' - description: Allocation status - - name: tx_power_dbm - kind: Text - optional: true - description: Measured transmit power in dBm (e.g., '0.5') - - name: rx_power_dbm - kind: Text - optional: true - description: Measured receive power in dBm (e.g., '-2.3') - - name: osnr_db - kind: Text - optional: true - description: Optical Signal-to-Noise Ratio in dB (e.g., '25.5') - - name: allocated_date - kind: DateTime - optional: true - description: Date channel was allocated - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: '{{ fiber_link__link_id__value }}-{{ channel__channel_name__value - }}' - description: Auto-generated mapping identifier - - name: description - kind: Text - optional: true - description: Additional fiber mapping details - relationships: - - name: fiber_link - peer: DcimFiberLink - cardinality: one - kind: Attribute - optional: false - identifier: fiber_channel_mappings - description: Long-haul fiber link connecting the degrees -- name: OpticalNode - namespace: Dcim - include_in_menu: true - label: Optical Node - description: Logical network endpoint in optical topology (graph node, not physical - device) - icon: mdi:lan - inherit_from: - - DcimOpticalDevice - display_label: computed_name__value - human_friendly_id: - - name__value - uniqueness_constraints: - - - name__value - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: '{{ name__value }}' - description: Computed name for display - - name: name - kind: Text - optional: false - description: Logical node name (e.g., GENESIS-OL-10, SKYLINE-OL-1) - - name: node_type - kind: Dropdown - optional: false - choices: - - name: endpoint - label: Endpoint - description: Edge node (traffic originates/terminates) - color: '#2196f3' - - name: intermediate - label: Intermediate - description: Pass-through node (ROADM only) - color: '#9c27b0' - - name: branching - label: Branching - description: Multiple fiber paths meet (hub) - color: '#ff9800' - description: Topology role of this node - - name: description - kind: Text - optional: true - description: Node notes and function - relationships: - - name: device - peer: DcimPhysicalDevice - cardinality: one - kind: Attribute - optional: true - identifier: optical_nodes__physical_device - description: Physical chassis that implements this logical node - - name: transponder_modules - peer: DcimTransponderModule - cardinality: many - kind: Attribute - optional: true - identifier: optical_node__transponder_modules - description: Transponder modules connected to this optical node (for cross-connects) - - name: links - peer: DcimFiberLink - cardinality: many - kind: Component - optional: true - identifier: optical_node__links - description: Fiber links connected to this node -- name: PassiveMultiplexer - namespace: Dcim - include_in_menu: true - label: Passive Multiplexer - description: Passive CWDM/DWDM multiplexer (fixed port count, no modules) - icon: mdi:resistor-nodes - inherit_from: - - DcimPhysicalDevice - display_label: computed_name__value - human_friendly_id: - - name__value - uniqueness_constraints: - - - name__value - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: '{{ name__value }}' - description: Computed name for display - - name: name - kind: Text - optional: false - description: Passive mux name (e.g., CHI-PMUX-01) - - name: port_count - kind: Number - optional: false - description: Number of fixed ports (e.g., 8, 16, 32, 40) - - name: mux_type - kind: Dropdown - optional: false - choices: - - name: cwdm - label: CWDM - description: Coarse Wavelength Division Multiplexing - color: '#4a90e2' - - name: dwdm - label: DWDM - description: Dense Wavelength Division Multiplexing - color: '#9933cc' - description: Type of passive multiplexer - - name: description - kind: Text - optional: true - description: Device notes -- name: FiberLink - namespace: Dcim - include_in_menu: true - label: Fiber Link - description: Fiber connecting two optical nodes (graph edge) - icon: mdi:cable-data - display_label: link_id__value - human_friendly_id: - - link_id__value - uniqueness_constraints: - - - link_id__value - attributes: - - name: link_id - kind: Text - optional: false - description: Unique link identifier (e.g., LINK-001) - - name: distance_km - kind: Text - optional: true - description: Physical distance in kilometers (e.g., '920.5') - - name: attenuation_db - kind: Text - optional: true - description: Total fiber attenuation in dB (e.g., '18.4') - - name: status - kind: Dropdown - optional: false - default_value: planned - choices: - - name: planned - label: Planned - description: Link is planned but not yet installed - color: '#95a5a6' - - name: active - label: Active - description: Link is in service - color: '#00cc00' - - name: standby - label: Standby - description: Link is installed but not active - color: '#f5a623' - - name: maintenance - label: Maintenance - description: Link is under maintenance - color: '#ff9800' - - name: failed - label: Failed - description: Link has failed - color: '#cc0000' - description: Operational status of fiber link - - name: commissioned_date - kind: DateTime - optional: true - description: Date link was commissioned - - name: description - kind: Text - optional: true - description: Additional link details - relationships: - - name: endpoints - peer: DcimOpticalNode - identifier: optical_node__links - cardinality: many - kind: Attribute - optional: false - max_count: 2 - description: Port-level endpoints participating in this link - - name: mappings - peer: DcimFiberMapping - cardinality: many - kind: Component - identifier: fiber_channel_mappings - optional: true - description: DWDM channels allocated on this link -- name: OpticalModuleType - namespace: Dcim - include_in_menu: true - label: Optical Module Type - description: Categorization of optical modules by function - icon: mdi:expansion-card - inherit_from: - - DcimGenericModuleType - attributes: - - name: name - kind: Text - optional: false - description: Module type name - - name: description - kind: Text - optional: true - description: Module type description - - name: module_category - kind: Dropdown - optional: false - description: Functional category of the module - choices: - - name: transponder - label: Transponder - description: Optical-electrical-optical conversion - color: '#2196f3' - - name: multiplexer - label: Multiplexer/ROADM - description: Wavelength multiplexing and switching - color: '#9c27b0' - - name: amplifier - label: Amplifier - description: Optical signal amplification - color: '#ff9800' - - name: monitoring - label: Monitoring - description: Optical performance monitoring - color: '#4caf50' - - name: control - label: Control - description: System control and management - color: '#607d8b' - - name: power - label: Power Supply - description: Power management modules - color: '#795548' - - name: cooling - label: Cooling - description: Thermal management - color: '#00bcd4' -- name: TransponderModule - namespace: Dcim - include_in_menu: true - label: Transponder Module - description: Optical-electrical-optical conversion module (100G, 400G coherent) - installed in chassis - icon: mdi:chip - inherit_from: - - DcimGenericModule - display_label: computed_name__value - uniqueness_constraints: - - - computed_name__value - human_friendly_id: - - computed_name__value - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: '{{ module_bay__computed_name__value }}-TPD-{{ slot_number__value - }}' - description: Computed module name (e.g., dc1-leaf1 > slot 1-TPD-1/1) - - name: slot_number - kind: Text - optional: false - description: Slot number in chassis (e.g., '1/1', '1/2', '2/1') - - name: capacity_gbps - kind: Number - optional: false - description: Line rate capacity in Gbps (e.g., 100, 200, 400) - - name: modulation_format - kind: Dropdown - optional: false - choices: - - name: dp_qpsk - label: DP-QPSK - description: Dual-polarization QPSK (100G) - color: '#2196f3' - - name: dp_16qam - label: DP-16QAM - description: Dual-polarization 16QAM (200G) - color: '#9c27b0' - - name: dp_8qam - label: DP-8QAM - description: Dual-polarization 8QAM (150G) - color: '#ff9800' - - name: dp_64qam - label: DP-64QAM - description: Dual-polarization 64QAM (400G) - color: '#4caf50' - description: Modulation format for optical transmission - - name: tunable_range - kind: Dropdown - optional: false - choices: - - name: c_band - label: C-Band (1530-1565nm) - description: Full C-Band tunable - color: '#9933cc' - - name: c_l_band - label: C+L-Band (1530-1625nm) - description: Extended C+L-Band tunable - color: '#cc33cc' - - name: fixed - label: Fixed Wavelength - description: Non-tunable, fixed wavelength - color: '#95a5a6' - description: Wavelength tuning capability - - name: fec_type - kind: Dropdown - optional: true - choices: - - name: sd_fec - label: SD-FEC - description: Soft-decision FEC (7% overhead) - color: '#2196f3' - - name: hd_fec - label: HD-FEC - description: Hard-decision FEC (25% overhead) - color: '#ff9800' - - name: c_fec - label: C-FEC - description: Concatenated FEC - color: '#4caf50' - - name: o_fec - label: O-FEC - description: OpenFEC - color: '#9c27b0' - description: Forward error correction type - - name: client_interface - kind: Text - optional: true - description: Client-side interface (e.g., 100GE, 400GE, OTU4) - - name: line_interface - kind: Text - optional: true - description: Line-side interface (e.g., CFP2-DCO, QSFP28, QSFP-DD) - relationships: - - name: tuned_channel - peer: DcimDWDMChannel - cardinality: one - kind: Attribute - optional: true - identifier: dwdm_channel__transponder_modules - description: DWDM channel currently tuned/configured - - name: connected_to_optical_node - peer: DcimOpticalNode - cardinality: one - kind: Attribute - optional: true - identifier: optical_node__transponder_modules - description: Optical node this transponder module connects to (for cross-connects) -- name: OpticalMultiplexerModule - namespace: Dcim - include_in_menu: true - label: Optical Multiplexer Module - description: Multiplexer/Demultiplexer/ROADM module for wavelength management installed - in chassis - icon: mdi:router-network - inherit_from: - - DcimGenericModule - display_label: computed_name__value - human_friendly_id: - - computed_name__value - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: '{{ module_bay__computed_name__value }}-MUX-{{ slot_number__value - }}' - description: Computed module name (e.g., dc1-leaf1 > slot 1-MUX-1/3) - - name: slot_number - kind: Text - optional: false - description: Slot number in chassis (e.g., '1/3', '1/4', '2/3') - - name: mux_type - kind: Dropdown - optional: false - choices: - - name: passive_mux - label: Passive Mux - description: Passive wavelength multiplexer (fixed) - color: '#95a5a6' - - name: passive_demux - label: Passive Demux - description: Passive wavelength demultiplexer (fixed) - color: '#7f8c8d' - - name: oadm - label: OADM - description: Optical Add-Drop Multiplexer - color: '#3498db' - - name: roadm - label: ROADM - description: Reconfigurable Optical Add-Drop Multiplexer - color: '#9b59b6' - - name: wss - label: WSS - description: Wavelength Selective Switch - color: '#e74c3c' - description: Type of multiplexer technology - - name: channel_capacity - kind: Number - optional: false - description: Number of supported DWDM channels (e.g., 40, 80, 96) - - name: technology - kind: Dropdown - optional: true - choices: - - name: thin_film_filter - label: Thin Film Filter - description: Passive thin film filter technology - color: '#95a5a6' - - name: awg - label: AWG - description: Arrayed Waveguide Grating - color: '#3498db' - - name: wss_lcos - label: WSS (LCOS) - description: Wavelength Selective Switch (Liquid Crystal on Silicon) - color: '#9b59b6' - - name: wss_mems - label: WSS (MEMS) - description: Wavelength Selective Switch (Micro-Electro-Mechanical Systems) - color: '#e74c3c' - description: Underlying multiplexer technology - - name: degree_count - kind: Number - optional: true - description: Number of ROADM degrees (e.g., 2, 4, 8, 16) - - name: colorless - kind: Boolean - optional: true - default_value: false - description: Colorless add/drop capability (any channel on any port) - - name: directionless - kind: Boolean - optional: true - default_value: false - description: Directionless add/drop capability (any port to any degree) - - name: contentionless - kind: Boolean - optional: true - default_value: false - description: Contentionless add/drop (CDC - Colorless, Directionless, Contentionless) - relationships: - - name: degrees - peer: DcimRoadmDegree - cardinality: many - kind: Component - optional: true - identifier: optical_multiplexer_module__degrees - description: ROADM degrees (line ports) on this multiplexer module - - name: mappings - peer: DcimChannelMapping - cardinality: many - kind: Attribute - optional: true - identifier: roadm_module__channel_mappings - description: Channel mappings (DirectConnect, CableMapping, FiberMapping) associated - with this module -- name: RoadmDegree - namespace: Dcim - include_in_menu: true - label: ROADM Degree - description: ROADM degree with line port (OL-1, OL-2, etc.) - icon: mdi:lan-connect - display_label: computed_name__value - human_friendly_id: - - computed_name__value - uniqueness_constraints: - - - roadm - - degree_number__value - - - roadm - - line_port__value - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: '{{ roadm__computed_name__value }}-{{ line_port__value }}' - description: Computed name (e.g., ROADM-SITE-A-OL-1) - - name: degree_number - kind: Number - optional: false - description: Degree number (1-based, e.g., 1, 2, 3, 4) - - name: line_port - kind: Text - optional: false - description: Line port designation (e.g., OL-1, OL-2, OL-7) - - name: direction - kind: Dropdown - optional: false - choices: - - name: north - label: North - description: Northbound direction - color: '#3498db' - - name: south - label: South - description: Southbound direction - color: '#e74c3c' - - name: east - label: East - description: Eastbound direction - color: '#2ecc71' - - name: west - label: West - description: Westbound direction - color: '#f39c12' - - name: express - label: Express - description: Express port (bypass) - color: '#9b59b6' - - name: local - label: Local - description: Local add/drop - color: '#95a5a6' - description: Directional designation of degree - - name: degree_type - kind: Dropdown - optional: false - choices: - - name: line - label: Line - description: Line port (connects to fiber link) - color: '#2196f3' - - name: express - label: Express - description: Express port (R-x ports, bypass) - color: '#9c27b0' - - name: add_drop - label: Add/Drop - description: Local add/drop for transponders - color: '#4caf50' - description: Type of ROADM degree - - name: wavelength_capacity - kind: Number - optional: true - description: Number of wavelengths supported on this degree - - name: description - kind: Text - optional: true - description: Additional degree details - relationships: - - name: roadm - peer: DcimOpticalMultiplexerModule - cardinality: one - kind: Parent - optional: false - identifier: optical_multiplexer_module__degrees - description: Parent ROADM module containing this degree - - name: connected_fiber - peer: DcimFiberLink - cardinality: one - kind: Attribute - optional: true - identifier: fiber_link__roadm_degree - description: Fiber link connected to this line port - - name: channel_mappings - peer: DcimChannelMapping - cardinality: many - kind: Attribute - optional: true - identifier: roadm_degree__channel_mappings - description: Channel mappings involving this degree -- name: WSSConnect - namespace: Dcim - inherit_from: - - DcimChannelMapping - include_in_menu: true - label: WSS Connect - description: Internal WSS cross-connect between degrees on SAME ROADM device (0m - distance) - icon: mdi:swap-horizontal - display_label: computed_name__value - human_friendly_id: - - computed_name__value - uniqueness_constraints: - - - roadm - - channel - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: '{{ roadm__computed_name__value }}-{{ channel__channel_name__value - }}' - description: Computed connection ID - - name: description - kind: Text - optional: true - description: Additional cross-connect details - relationships: - - name: roadm - peer: DcimOpticalMultiplexerModule - cardinality: one - kind: Attribute - optional: false - identifier: roadm_module__channel_mappings - description: Parent ROADM device -- name: CableMapping - namespace: Dcim - inherit_from: - - DcimChannelMapping - include_in_menu: true - label: Cable Mapping - description: Local patch cable between degrees on DIFFERENT ROADMs (meters distance, - same site) - icon: mdi:cable-data - display_label: computed_name__value - human_friendly_id: - - computed_name__value - uniqueness_constraints: - - - cable - - channel - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: '{{ cable__label__value }}-{{ channel__channel_name__value - }}' - description: Computed mapping name (e.g., CABLE-123-CH58) - - name: description - kind: Text - optional: true - description: Additional cable mapping details - relationships: - - name: cable - peer: DcimCable - cardinality: one - kind: Attribute - optional: false - identifier: cable__channel_mappings - description: Physical patch cable connecting the degrees -- name: OpticalAmplifierModule - namespace: Dcim - include_in_menu: true - label: Optical Amplifier Module - description: Optical signal amplifier module (EDFA, Raman, VGC) installed in chassis - icon: mdi:amplifier - inherit_from: - - DcimGenericModule - display_label: computed_name__value - human_friendly_id: - - computed_name__value - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: '{{ module_bay__computed_name__value }}-AMP-{{ slot_number__value - }}' - description: Computed module name (e.g., dc1-leaf1 > slot 1-AMP-1/5) - - name: slot_number - kind: Text - optional: false - description: Slot number in chassis (e.g., '1/5', '1/6', '2/5') - - name: amplifier_type - kind: Dropdown - optional: false - choices: - - name: edfa - label: EDFA - description: Erbium-Doped Fiber Amplifier - color: '#2196f3' - - name: raman - label: Raman - description: Raman Amplifier (distributed) - color: '#9c27b0' - - name: soa - label: SOA - description: Semiconductor Optical Amplifier - color: '#ff9800' - - name: vgc - label: VGC - description: Variable Gain Controller - color: '#4caf50' - - name: hybrid - label: Hybrid - description: Hybrid amplifier (e.g., EDFA + Raman) - color: '#607d8b' - description: Type of optical amplifier - - name: stage - kind: Dropdown - optional: false - choices: - - name: booster - label: Booster - description: Power amplifier (transmit side) - color: '#e74c3c' - - name: inline - label: Inline - description: In-line amplifier (along fiber span) - color: '#3498db' - - name: preamplifier - label: Pre-Amplifier - description: Low-noise amplifier (receive side) - color: '#2ecc71' - description: Amplifier stage position - - name: gain_db - kind: Text - optional: true - description: Typical gain in dB (e.g., '20.0', '17.5') - - name: max_output_power_dbm - kind: Text - optional: true - description: Maximum output power in dBm (e.g., '17.0', '23.0') - - name: noise_figure_db - kind: Text - optional: true - description: Noise figure in dB (e.g., '5.5', '4.8') - - name: operating_band - kind: Dropdown - optional: false - choices: - - name: c_band - label: C-Band (1530-1565nm) - description: C-Band operation - color: '#9933cc' - - name: l_band - label: L-Band (1565-1625nm) - description: L-Band operation - color: '#cc33cc' - - name: c_l_band - label: C+L-Band - description: Dual-band operation - color: '#ff33cc' - description: Operating wavelength band - relationships: - - name: amplified_link - peer: DcimFiberLink - cardinality: one - kind: Attribute - optional: true - identifier: fiber_link__amplifier_modules - description: Fiber link being amplified (for inline amplifiers) -- name: OpticalService - namespace: Dcim - include_in_menu: true - label: Optical Service - description: End-to-end optical transport service (customer circuit) spanning multiple - sites - icon: mdi:transit-connection-variant - display_label: service_name__value - human_friendly_id: - - service_id__value - uniqueness_constraints: - - - service_id__value - order_by: - - service_id__value - attributes: - - name: service_id - kind: Text - optional: false - description: Unique service identifier - - name: service_name - kind: Text - optional: false - description: 'Customer service name (e.g., ''ATC 10G - Albion to Arco #2'')' - - name: service_type - kind: Dropdown - optional: false - choices: - - name: wavelength - label: Wavelength Service - description: Dedicated wavelength end-to-end - color: '#9c27b0' - - name: transport - label: Transport Service - description: Layer 1 optical transport - color: '#2196f3' - - name: ip_transit - label: IP Transit - description: Layer 3 IP service over optical - color: '#4caf50' - description: Type of optical service - - name: bandwidth - kind: Text - optional: false - description: Service bandwidth (e.g., '10G', '100G', '400G') - - name: status - kind: Dropdown - optional: false - default_value: planned - choices: - - name: planned - label: Planned - description: Service planned but not provisioned - color: '#95a5a6' - - name: provisioned - label: Provisioned - description: Service configured but not active - color: '#f5a623' - - name: active - label: Active - description: Service in production - color: '#00cc00' - - name: maintenance - label: Maintenance - description: Service under maintenance - color: '#ff9800' - - name: failed - label: Failed - description: Service down - color: '#cc0000' - description: Service operational status - - name: customer_circuit_id - kind: Text - optional: true - description: Customer's circuit ID (e.g., '99/OKFS/000029//SYG') - - name: provision_date - kind: DateTime - optional: true - description: Date service was provisioned - - name: description - kind: Text - optional: true - description: Additional service details - relationships: - - name: transponders - peer: DcimTransponderModule - cardinality: many - min_count: 2 - max_count: 2 - kind: Attribute - optional: false - identifier: transponder_module__optical_services - description: Two transponders (endpoints) for this service (bidirectional) - - name: channel - peer: DcimDWDMChannel - cardinality: one - kind: Attribute - optional: false - identifier: dwdm_channel__services - description: DWDM channel used for this service - - name: primary_path - peer: DcimOpticalPath - cardinality: one - kind: Component - optional: false - identifier: optical_service__primary_path - description: Primary optical path for this service - - name: backup_path - peer: DcimOpticalPath - cardinality: one - kind: Component - optional: true - identifier: optical_service__backup_path - description: Backup/protection path for this service -- name: OpticalPath - namespace: Dcim - include_in_menu: true - label: Optical Path - description: Ordered sequence of segments forming an optical path through the network - icon: mdi:map-marker-path - display_label: name__value - human_friendly_id: - - name__value - attributes: - - name: name - kind: Text - optional: false - unique: true - description: Path name (e.g., MS-W17D5-primary) - - name: path_type - kind: Dropdown - optional: false - choices: - - name: primary - label: Primary - description: Primary working path - color: '#2196f3' - - name: backup - label: Backup - description: Backup/protection path - color: '#ff9800' - - name: express - label: Express - description: Express bypass path - color: '#9c27b0' - description: Type of path (primary, backup, express) - - name: total_distance_km - kind: Text - optional: true - description: Total path distance in kilometers - - name: total_loss_db - kind: Text - optional: true - description: Total optical loss in dB - - name: hop_count - kind: Number - optional: true - description: Number of ROADM hops in path - - name: is_active - kind: Boolean - optional: false - default_value: true - description: Whether this path is currently active - relationships: - - name: segments - peer: DcimPathSegment - identifier: optical_path__segments - cardinality: many - kind: Component - optional: true - description: Ordered segments forming this path - - name: is_primary_of_service - peer: DcimOpticalService - identifier: optical_service__primary_path - cardinality: one - kind: Attribute - optional: true - description: Optical service using this path - - name: is_backup_of_service - peer: DcimOpticalService - identifier: optical_service__backup_path - cardinality: one - kind: Attribute - optional: true - description: Optical service using this path as backup -- name: PathSegment - namespace: Dcim - include_in_menu: true - label: Path Segment - description: One hop in optical path - references channel mapping (fiber, cable, - or cross-connect) - icon: mdi:ray-start-arrow - display_label: computed_name__value - human_friendly_id: - - computed_name__value - uniqueness_constraints: - - - path - - segment_order__value - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: '{{ path__name__value }}-SEG{{ segment_order__value }}' - description: Computed segment name (e.g., MS-W17D5-primary-SEG1) - - name: segment_order - kind: Number - optional: false - description: Order of this segment in the path (1, 2, 3...) - - name: segment_type - kind: Dropdown - optional: false - choices: - - name: fiber - label: Fiber - description: Segment traverses a long-haul fiber link - color: '#2196f3' - - name: cross_connect - label: Cross-Connect - description: Segment crosses through ROADM degrees (internal WSS) - color: '#9c27b0' - - name: cable - label: Cable - description: Segment traverses a local patch cable - color: '#ff9800' - - name: add - label: Add - description: Service added at this point (A-end) - color: '#4caf50' - - name: drop - label: Drop - description: Service dropped at this point (Z-end) - color: '#ff5722' - description: Type of segment - - name: loss_db - kind: Text - optional: true - description: Optical loss for this segment in dB - relationships: - - name: path - peer: DcimOpticalPath - cardinality: one - kind: Parent - optional: false - identifier: optical_path__segments - description: Parent path containing this segment - - name: channel_mapping - peer: DcimChannelMapping - cardinality: one - kind: Attribute - on_delete: cascade - optional: true - identifier: channel_mapping__path_segments - description: Channel mapping (WSS Connect, Cable Mapping, or Fiber Mapping) -extensions: - nodes: - - kind: DcimPhysicalDevice - relationships: - - name: optical_node - peer: DcimOpticalNode - kind: Generic - optional: true - cardinality: many - identifier: optical_nodes__physical_device - -``` \ No newline at end of file diff --git a/experimental/optical_transport/README.md b/experimental/optical_transport/README.md deleted file mode 100644 index 6b5478c3..00000000 --- a/experimental/optical_transport/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# optical_transport - -Please refer to the [reference page](https://docs.infrahub.app/schema-library/reference/optical_transport) for the corresponding documentation. diff --git a/experimental/optical_transport/optical_transport.yml b/experimental/optical_transport/optical_transport.yml deleted file mode 100644 index 0ecb9e65..00000000 --- a/experimental/optical_transport/optical_transport.yml +++ /dev/null @@ -1,1500 +0,0 @@ -# yaml-language-server: $schema=https://schema.infrahub.app/infrahub/schema/latest.json -# --------------------------------------------------------------------------- -# This schema is a starting point for building with Infrahub, not a finished production model. -# Adapting it for your environment typically requires architectural review, see -# docs.infrahub.app, or reach out to OpsMill for help. -# --------------------------------------------------------------------------- ---- -version: "1.0" - -# Optical Transport Network Schemas for DWDM/WDM systems -# -# Depends on: -# - base/dcim.yml (DcimGenericDevice, DcimPhysicalDevice, DcimEndpoint, DcimConnector) -# - extensions/device_module/device_module.yml (DcimGenericModule, DcimGenericModuleType, DcimModuleBay) -# - extensions/cable/cable.yml (DcimCable) -# -# Four layers: -# 1. Wavelength - ITU-T G.694.1 grid, optical bands, DWDM channels -# 2. Topology - Logical optical nodes, passive multiplexers, fiber links -# 3. Equipment - Transponder/amplifier/ROADM modules, degrees, cross-connects -# 4. Service - End-to-end optical services, paths, segments - -generics: - # ============================================================================ - # ChannelMapping - Generic for any channel allocation/routing - # ============================================================================ - - name: ChannelMapping # DcimChannelMapping - namespace: Dcim - description: "Generic base for any channel allocation/routing (fiber, cable, or internal cross-connect)" - label: "Channel Mapping" - include_in_menu: false - attributes: - # TODO: Replace with a proper status - - name: is_active - kind: Boolean - optional: false - default_value: true - description: "Whether this mapping is currently active" - - name: configured_date - kind: DateTime - optional: true - description: "Date this mapping was configured" - relationships: - - name: segment - peer: DcimPathSegment - cardinality: one - kind: Parent - optional: false - identifier: "channel_mapping__path_segments" - description: "Optical Path segment associated with this mapping" - - name: channel - peer: DcimDWDMChannel - cardinality: one - kind: Attribute - optional: false - identifier: "dwdm_channel__mappings" - description: "DWDM channel being mapped/routed" - - name: degrees - peer: DcimRoadmDegree - cardinality: many - max_count: 2 - kind: Attribute - optional: false - identifier: "roadm_degree__channel_mappings" - description: "Two ROADM degrees involved in this mapping (bidirectional)" - - # ============================================================================ - # OpticalDevice - Generic for any device that can multiplex channels - # ============================================================================ - - name: OpticalDevice # DcimOpticalDevice - namespace: Dcim - description: "Generic interface for any device that can multiplex DWDM channels (active or passive)" - label: "Optical Multiplexer Device" - include_in_menu: false - uniqueness_constraints: - - [computed_name__value] - attributes: - # Placeholder, will be overridden in child nodes - - name: computed_name - kind: Text - optional: false - -# ============================================================================== -# NODES -# ============================================================================== -nodes: - # ============================================================================ - # Wavelength Layer - ITU-T G.694.1 Grid and Channels - # ============================================================================ - - # ============================================================================ - # ITUGrid - ITU-T G.694.1 DWDM Grid Standards - # ============================================================================ - - name: ITUGrid # DcimITUGrid - namespace: Dcim - include_in_menu: true - label: "ITU-T DWDM Grid" - description: "ITU-T G.694.1 frequency grid standards for DWDM" - icon: mdi:grid - display_label: name__value - human_friendly_id: - - name__value - uniqueness_constraints: - - [name__value] - attributes: - - name: name - kind: Text - optional: false - description: "Grid name (e.g., 50GHz, 100GHz)" - - name: channel_spacing_ghz - kind: Number - optional: false - description: "Channel spacing in GHz (50, 100, etc.)" - - name: reference_frequency_thz - kind: Text - optional: false - description: "Reference frequency in THz (e.g., '193.1' for 1550nm)" - - name: description - kind: Text - optional: true - description: "Grid description and use cases" - relationships: - - name: bands - peer: DcimOpticalBand - cardinality: many - kind: Component - optional: true - description: "Optical bands using this grid" - - # ============================================================================ - # OpticalBand - C-Band, L-Band, etc. - # ============================================================================ - - name: OpticalBand # DcimOpticalBand - namespace: Dcim - include_in_menu: true - label: "Optical Band" - description: "Optical frequency band (C-Band, L-Band, S-Band)" - icon: mdi:sine-wave - display_label: computed_name__value - human_friendly_id: [grid__name__value, band_name__value] - uniqueness_constraints: - - [grid, band_name__value] - attributes: - - name: band_name - kind: Dropdown - optional: false - choices: - - name: c_band - label: "C-Band" - description: "Conventional Band (1530-1565 nm)" - color: "#ff6b6b" - - name: l_band - label: "L-Band" - description: "Long Band (1565-1625 nm)" - color: "#4ecdc4" - - name: s_band - label: "S-Band" - description: "Short Band (1460-1530 nm)" - color: "#95e1d3" - - name: o_band - label: "O-Band" - description: "Original Band (1260-1360 nm)" - color: "#f38181" - description: "Optical band designation" - - name: start_frequency_thz - kind: Text - optional: false - description: "Band start frequency in THz (e.g., '191.0')" - - name: end_frequency_thz - kind: Text - optional: false - description: "Band end frequency in THz (e.g., '196.1')" - - name: start_wavelength_nm - kind: Text - optional: true - description: "Band start wavelength in nm (e.g., '1530.0')" - - name: end_wavelength_nm - kind: Text - optional: true - description: "Band end wavelength in nm (e.g., '1565.0')" - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: "{{ grid__name__value }}-{{ band_name__value|upper }}" - description: "Auto-generated band identifier (e.g., 50GHz-C_BAND)" - relationships: - - name: grid - peer: DcimITUGrid - cardinality: one - kind: Parent - optional: false - description: "Parent ITU grid" - - name: channels - peer: DcimDWDMChannel - cardinality: many - kind: Component - optional: true - description: "DWDM channels in this band" - - # ============================================================================ - # DWDMChannel - Individual Wavelength/Channel - # ============================================================================ - - name: DWDMChannel # DcimDWDMChannel - namespace: Dcim - include_in_menu: true - label: "DWDM Channel" - description: "Individual DWDM wavelength/channel (CH20, CH21, etc.)" - icon: mdi:wave - display_label: channel_name__value - human_friendly_id: - - channel_name__value - order_by: - - band__computed_name__value - - channel_number__value - uniqueness_constraints: - - [band, channel_number__value] - - [channel_name__value] - - [channel_number__value, frequency_thz__value, wavelength_nm__value] - attributes: - - name: channel_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: "CH{{ channel_number__value }}" - description: "ITU DWDM Channel identifier (computed as CH + channel number, e.g., CH20, CH21, CH40)" - - name: adva_channel_name - kind: Text - optional: true - description: "ADVA-specific channel name (e.g., D02, DC1, D32)" - - name: channel_number - kind: Number - optional: false - description: "ITU channel number (e.g., 20, 21, 60)" - - name: frequency_thz - kind: Text - optional: false - description: "Center frequency in THz (e.g., '192.1')" - - name: wavelength_nm - kind: Text - optional: false - description: "Center wavelength in nm (e.g., '1560.61')" - - name: description - kind: Text - optional: true - description: "Channel notes and usage" - relationships: - - name: band - peer: DcimOpticalBand - cardinality: one - kind: Parent - optional: false - description: "Parent optical band" - - name: mux_assignments - peer: DcimMuxChannelAssignment - cardinality: many - kind: Component - optional: true - description: "Mux port assignments for this channel" - - name: mappings - peer: DcimChannelMapping - cardinality: many - kind: Generic - optional: true - description: "Fiber allocations for this channel" - - # ============================================================================ - # MuxChannelAssignment - Channel to Mux Port Mapping - # ============================================================================ - - name: MuxChannelAssignment # DcimMuxChannelAssignment - namespace: Dcim - include_in_menu: true - label: "Mux Channel Assignment" - description: "Maps DWDM channel to multiplexer port (equipment configuration)" - icon: mdi:cable-data - display_label: computed_name__value - human_friendly_id: - - optical_device__computed_name__value - - port_number__value - uniqueness_constraints: - - [optical_device, port_number__value] - - [optical_device, channel] - attributes: - - name: port_number - kind: Number - optional: false - description: "Physical port number on multiplexer (e.g., 1-96)" - - name: tx_power_dbm - kind: Text - optional: true - description: "Configured transmit power in dBm (e.g., '0.0')" - - name: status - kind: Dropdown - optional: false - default_value: "configured" - choices: - - name: configured - label: "Configured" - description: "Channel is configured on port" - color: "#f5a623" - - name: active - label: "Active" - description: "Channel is actively transmitting" - color: "#00cc00" - - name: disabled - label: "Disabled" - description: "Channel is disabled" - color: "#cccccc" - - name: failed - label: "Failed" - description: "Channel has failed" - color: "#cc0000" - description: "Channel assignment status" - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: >- - {{ optical_device__computed_name__value - }}-P{{ '{:02d}'.format(port_number__value) - }}-{{ channel__channel_name__value }} - description: "Auto-generated assignment identifier" - relationships: - - name: channel - peer: DcimDWDMChannel - cardinality: one - kind: Attribute - optional: false - description: "DWDM channel assigned to this port" - - name: optical_device - peer: DcimOpticalDevice - cardinality: one - kind: Parent - optional: false - identifier: "mux_channel_assignments" - description: "Multiplexer device (OpticalNode or PassiveMultiplexer)" - - # ============================================================================ - # FiberMapping - Channel Allocation on Long-Haul Fiber - # ============================================================================ - - name: FiberMapping # DcimFiberMapping - namespace: Dcim - inherit_from: - - DcimChannelMapping - include_in_menu: true - label: "Fiber Mapping" - description: "Long-haul fiber link between degrees on DIFFERENT ROADMs/sites (kilometers distance)" - icon: mdi:fiber-optic - display_label: computed_name__value - human_friendly_id: - - fiber_link__link_id__value - - channel__channel_name__value - uniqueness_constraints: - - [fiber_link, channel] - attributes: - - name: allocation_status - kind: Dropdown - optional: false - default_value: "reserved" - choices: - - name: reserved - label: "Reserved" - description: "Channel reserved but not in use" - color: "#f5a623" - - name: allocated - label: "Allocated" - description: "Channel allocated to service" - color: "#0099cc" - - name: in_use - label: "In Use" - description: "Channel actively carrying traffic" - color: "#00cc00" - - name: maintenance - label: "Maintenance" - description: "Channel under maintenance" - color: "#ff9800" - - name: failed - label: "Failed" - description: "Channel has failed" - color: "#cc0000" - description: "Allocation status" - - name: tx_power_dbm - kind: Text - optional: true - description: "Measured transmit power in dBm (e.g., '0.5')" - - name: rx_power_dbm - kind: Text - optional: true - description: "Measured receive power in dBm (e.g., '-2.3')" - - name: osnr_db - kind: Text - optional: true - description: "Optical Signal-to-Noise Ratio in dB (e.g., '25.5')" - - name: allocated_date - kind: DateTime - optional: true - description: "Date channel was allocated" - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: "{{ fiber_link__link_id__value }}-{{ channel__channel_name__value }}" - description: "Auto-generated mapping identifier" - - name: description - kind: Text - optional: true - description: "Additional fiber mapping details" - relationships: - - name: fiber_link - peer: DcimFiberLink - cardinality: one - kind: Attribute - optional: false - identifier: "fiber_channel_mappings" - description: "Long-haul fiber link connecting the degrees" - - # ============================================================================ - # Topology Layer - Optical Nodes and Fiber Links - # ============================================================================ - - # ============================================================================ - # OpticalNode - Logical Network Topology Element - # ============================================================================ - - name: OpticalNode # DcimOpticalNode - namespace: Dcim - include_in_menu: true - label: "Optical Node" - description: "Logical network endpoint in optical topology (graph node, not physical device)" - icon: mdi:lan - inherit_from: - - DcimOpticalDevice - display_label: computed_name__value - human_friendly_id: - - name__value - uniqueness_constraints: - - [name__value] - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: "{{ name__value }}" - description: "Computed name for display" - - name: name - kind: Text - optional: false - description: "Logical node name (e.g., GENESIS-OL-10, SKYLINE-OL-1)" - - name: node_type - kind: Dropdown - optional: false - choices: - - name: endpoint - label: "Endpoint" - description: "Edge node (traffic originates/terminates)" - color: "#2196f3" - - name: intermediate - label: "Intermediate" - description: "Pass-through node (ROADM only)" - color: "#9c27b0" - - name: branching - label: "Branching" - description: "Multiple fiber paths meet (hub)" - color: "#ff9800" - description: "Topology role of this node" - - name: description - kind: Text - optional: true - description: "Node notes and function" - relationships: - - name: device - peer: DcimPhysicalDevice - cardinality: one - kind: Attribute - optional: true - identifier: optical_nodes__physical_device - description: "Physical chassis that implements this logical node" - - name: transponder_modules - peer: DcimTransponderModule - cardinality: many - kind: Attribute - optional: true - identifier: optical_node__transponder_modules - description: "Transponder modules connected to this optical node (for cross-connects)" - - name: links - peer: DcimFiberLink - cardinality: many - kind: Component - optional: true - identifier: optical_node__links - description: "Fiber links connected to this node" - - # ============================================================================ - # PassiveMultiplexer - Standalone Passive Mux/Demux - # ============================================================================ - - name: PassiveMultiplexer # DcimPassiveMultiplexer - namespace: Dcim - include_in_menu: true - label: "Passive Multiplexer" - description: "Passive CWDM/DWDM multiplexer (fixed port count, no modules)" - icon: mdi:resistor-nodes - inherit_from: - - DcimPhysicalDevice - display_label: computed_name__value - human_friendly_id: - - name__value - uniqueness_constraints: - - [name__value] - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: "{{ name__value }}" - description: "Computed name for display" - - name: name - kind: Text - optional: false - description: "Passive mux name (e.g., CHI-PMUX-01)" - - name: port_count - kind: Number - optional: false - description: "Number of fixed ports (e.g., 8, 16, 32, 40)" - - name: mux_type - kind: Dropdown - optional: false - choices: - - name: cwdm - label: "CWDM" - description: "Coarse Wavelength Division Multiplexing" - color: "#4a90e2" - - name: dwdm - label: "DWDM" - description: "Dense Wavelength Division Multiplexing" - color: "#9933cc" - description: "Type of passive multiplexer" - - name: description - kind: Text - optional: true - description: "Device notes" - - # ============================================================================ - # FiberLink - Network Graph Edge - # ============================================================================ - - name: FiberLink # DcimFiberLink - namespace: Dcim - include_in_menu: true - label: "Fiber Link" - description: "Fiber connecting two optical nodes (graph edge)" - icon: mdi:cable-data - display_label: link_id__value - human_friendly_id: - - link_id__value - uniqueness_constraints: - - [link_id__value] - attributes: - - name: link_id - kind: Text - optional: false - description: "Unique link identifier (e.g., LINK-001)" - # FIXME: Text until float are supported in Number - - name: distance_km - kind: Text - optional: true - description: "Physical distance in kilometers (e.g., '920.5')" - # FIXME: Text until float are supported in Number - - name: attenuation_db - kind: Text - optional: true - description: "Total fiber attenuation in dB (e.g., '18.4')" - - name: status - kind: Dropdown - optional: false - default_value: "planned" - choices: - - name: planned - label: "Planned" - description: "Link is planned but not yet installed" - color: "#95a5a6" - - name: active - label: "Active" - description: "Link is in service" - color: "#00cc00" - - name: standby - label: "Standby" - description: "Link is installed but not active" - color: "#f5a623" - - name: maintenance - label: "Maintenance" - description: "Link is under maintenance" - color: "#ff9800" - - name: failed - label: "Failed" - description: "Link has failed" - color: "#cc0000" - description: "Operational status of fiber link" - - name: commissioned_date - kind: DateTime - optional: true - description: "Date link was commissioned" - - name: description - kind: Text - optional: true - description: "Additional link details" - relationships: - - name: endpoints - peer: DcimOpticalNode - identifier: optical_node__links - cardinality: many - # Keeping them as Attribute as we want to show both endpoints in link view - kind: Attribute - optional: false - max_count: 2 - description: "Port-level endpoints participating in this link" - - name: mappings - peer: DcimFiberMapping - cardinality: many - kind: Component - identifier: "fiber_channel_mappings" - optional: true - description: "DWDM channels allocated on this link" - - # ============================================================================ - # Equipment Layer - Modules and ROADM Degrees - # ============================================================================ - - # ============================================================================ - # OpticalModuleType - Specific optical module categorization - # ============================================================================ - - name: OpticalModuleType # DcimOpticalModuleType - namespace: Dcim - include_in_menu: true - label: "Optical Module Type" - description: "Categorization of optical modules by function" - icon: mdi:expansion-card - inherit_from: - - DcimGenericModuleType - attributes: - - name: name - kind: Text - optional: false - description: "Module type name" - - name: description - kind: Text - optional: true - description: "Module type description" - - name: module_category - kind: Dropdown - optional: false - description: "Functional category of the module" - choices: - - name: transponder - label: "Transponder" - description: "Optical-electrical-optical conversion" - color: "#2196f3" - - name: multiplexer - label: "Multiplexer/ROADM" - description: "Wavelength multiplexing and switching" - color: "#9c27b0" - - name: amplifier - label: "Amplifier" - description: "Optical signal amplification" - color: "#ff9800" - - name: monitoring - label: "Monitoring" - description: "Optical performance monitoring" - color: "#4caf50" - - name: control - label: "Control" - description: "System control and management" - color: "#607d8b" - - name: power - label: "Power Supply" - description: "Power management modules" - color: "#795548" - - name: cooling - label: "Cooling" - description: "Thermal management" - color: "#00bcd4" - - # ============================================================================ - # TransponderModule - Wavelength Transmitter/Receiver Module - # ============================================================================ - - name: TransponderModule # DcimTransponderModule - namespace: Dcim - include_in_menu: true - label: "Transponder Module" - description: "Optical-electrical-optical conversion module (100G, 400G coherent) installed in chassis" - icon: mdi:chip - inherit_from: - - DcimGenericModule - display_label: computed_name__value - uniqueness_constraints: - - [computed_name__value] - human_friendly_id: - - computed_name__value - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: "{{ module_bay__computed_name__value }}-TPD-{{ slot_number__value }}" - description: "Computed module name (e.g., dc1-leaf1 > slot 1-TPD-1/1)" - - name: slot_number - kind: Text - optional: false - description: "Slot number in chassis (e.g., '1/1', '1/2', '2/1')" - - name: capacity_gbps - kind: Number - optional: false - description: "Line rate capacity in Gbps (e.g., 100, 200, 400)" - - name: modulation_format - kind: Dropdown - optional: false - choices: - - name: dp_qpsk - label: "DP-QPSK" - description: "Dual-polarization QPSK (100G)" - color: "#2196f3" - - name: dp_16qam - label: "DP-16QAM" - description: "Dual-polarization 16QAM (200G)" - color: "#9c27b0" - - name: dp_8qam - label: "DP-8QAM" - description: "Dual-polarization 8QAM (150G)" - color: "#ff9800" - - name: dp_64qam - label: "DP-64QAM" - description: "Dual-polarization 64QAM (400G)" - color: "#4caf50" - description: "Modulation format for optical transmission" - - name: tunable_range - kind: Dropdown - optional: false - choices: - - name: c_band - label: "C-Band (1530-1565nm)" - description: "Full C-Band tunable" - color: "#9933cc" - - name: c_l_band - label: "C+L-Band (1530-1625nm)" - description: "Extended C+L-Band tunable" - color: "#cc33cc" - - name: fixed - label: "Fixed Wavelength" - description: "Non-tunable, fixed wavelength" - color: "#95a5a6" - description: "Wavelength tuning capability" - - name: fec_type - kind: Dropdown - optional: true - choices: - - name: sd_fec - label: "SD-FEC" - description: "Soft-decision FEC (7% overhead)" - color: "#2196f3" - - name: hd_fec - label: "HD-FEC" - description: "Hard-decision FEC (25% overhead)" - color: "#ff9800" - - name: c_fec - label: "C-FEC" - description: "Concatenated FEC" - color: "#4caf50" - - name: o_fec - label: "O-FEC" - description: "OpenFEC" - color: "#9c27b0" - description: "Forward error correction type" - - name: client_interface - kind: Text - optional: true - description: "Client-side interface (e.g., 100GE, 400GE, OTU4)" - - name: line_interface - kind: Text - optional: true - description: "Line-side interface (e.g., CFP2-DCO, QSFP28, QSFP-DD)" - relationships: - - name: tuned_channel - peer: DcimDWDMChannel - cardinality: one - kind: Attribute - optional: true - identifier: "dwdm_channel__transponder_modules" - description: "DWDM channel currently tuned/configured" - - name: connected_to_optical_node - peer: DcimOpticalNode - cardinality: one - kind: Attribute - optional: true - identifier: optical_node__transponder_modules - description: "Optical node this transponder module connects to (for cross-connects)" - - # ============================================================================ - # OpticalMultiplexerModule - Mux/Demux/ROADM Module - # ============================================================================ - - name: OpticalMultiplexerModule # DcimOpticalMultiplexerModule - namespace: Dcim - include_in_menu: true - label: "Optical Multiplexer Module" - description: "Multiplexer/Demultiplexer/ROADM module for wavelength management installed in chassis" - icon: mdi:router-network - inherit_from: - - DcimGenericModule - display_label: computed_name__value - human_friendly_id: - - computed_name__value - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: "{{ module_bay__computed_name__value }}-MUX-{{ slot_number__value }}" - description: "Computed module name (e.g., dc1-leaf1 > slot 1-MUX-1/3)" - - name: slot_number - kind: Text - optional: false - description: "Slot number in chassis (e.g., '1/3', '1/4', '2/3')" - - name: mux_type - kind: Dropdown - optional: false - choices: - - name: passive_mux - label: "Passive Mux" - description: "Passive wavelength multiplexer (fixed)" - color: "#95a5a6" - - name: passive_demux - label: "Passive Demux" - description: "Passive wavelength demultiplexer (fixed)" - color: "#7f8c8d" - - name: oadm - label: "OADM" - description: "Optical Add-Drop Multiplexer" - color: "#3498db" - - name: roadm - label: "ROADM" - description: "Reconfigurable Optical Add-Drop Multiplexer" - color: "#9b59b6" - - name: wss - label: "WSS" - description: "Wavelength Selective Switch" - color: "#e74c3c" - description: "Type of multiplexer technology" - - name: channel_capacity - kind: Number - optional: false - description: "Number of supported DWDM channels (e.g., 40, 80, 96)" - - name: technology - kind: Dropdown - optional: true - choices: - - name: thin_film_filter - label: "Thin Film Filter" - description: "Passive thin film filter technology" - color: "#95a5a6" - - name: awg - label: "AWG" - description: "Arrayed Waveguide Grating" - color: "#3498db" - - name: wss_lcos - label: "WSS (LCOS)" - description: "Wavelength Selective Switch (Liquid Crystal on Silicon)" - color: "#9b59b6" - - name: wss_mems - label: "WSS (MEMS)" - description: "Wavelength Selective Switch (Micro-Electro-Mechanical Systems)" - color: "#e74c3c" - description: "Underlying multiplexer technology" - - name: degree_count - kind: Number - optional: true - description: "Number of ROADM degrees (e.g., 2, 4, 8, 16)" - - name: colorless - kind: Boolean - optional: true - default_value: false - description: "Colorless add/drop capability (any channel on any port)" - - name: directionless - kind: Boolean - optional: true - default_value: false - description: "Directionless add/drop capability (any port to any degree)" - - name: contentionless - kind: Boolean - optional: true - default_value: false - description: "Contentionless add/drop (CDC - Colorless, Directionless, Contentionless)" - relationships: - - name: degrees - peer: DcimRoadmDegree - cardinality: many - kind: Component - optional: true - identifier: "optical_multiplexer_module__degrees" - description: "ROADM degrees (line ports) on this multiplexer module" - - name: mappings - peer: DcimChannelMapping - cardinality: many - kind: Attribute - optional: true - identifier: "roadm_module__channel_mappings" - description: "Channel mappings (DirectConnect, CableMapping, FiberMapping) associated with this module" - - # ============================================================================ - # RoadmDegree - ROADM Degree with Line Port - # ============================================================================ - - name: RoadmDegree # DcimRoadmDegree - namespace: Dcim - include_in_menu: true - label: "ROADM Degree" - description: "ROADM degree with line port (OL-1, OL-2, etc.)" - icon: mdi:lan-connect - display_label: computed_name__value - human_friendly_id: - - computed_name__value - uniqueness_constraints: - - [roadm, degree_number__value] - - [roadm, line_port__value] - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: "{{ roadm__computed_name__value }}-{{ line_port__value }}" - description: "Computed name (e.g., ROADM-SITE-A-OL-1)" - - name: degree_number - kind: Number - optional: false - description: "Degree number (1-based, e.g., 1, 2, 3, 4)" - # TODO: Should we compute it based on degree_number and degree_type - - name: line_port - kind: Text - optional: false - description: "Line port designation (e.g., OL-1, OL-2, OL-7)" - - name: direction - kind: Dropdown - optional: false - choices: - - name: north - label: "North" - description: "Northbound direction" - color: "#3498db" - - name: south - label: "South" - description: "Southbound direction" - color: "#e74c3c" - - name: east - label: "East" - description: "Eastbound direction" - color: "#2ecc71" - - name: west - label: "West" - description: "Westbound direction" - color: "#f39c12" - - name: express - label: "Express" - description: "Express port (bypass)" - color: "#9b59b6" - - name: local - label: "Local" - description: "Local add/drop" - color: "#95a5a6" - description: "Directional designation of degree" - - name: degree_type - kind: Dropdown - optional: false - choices: - - name: line - label: "Line" - description: "Line port (connects to fiber link)" - color: "#2196f3" - - name: express - label: "Express" - description: "Express port (R-x ports, bypass)" - color: "#9c27b0" - - name: add_drop - label: "Add/Drop" - description: "Local add/drop for transponders" - color: "#4caf50" - description: "Type of ROADM degree" - - name: wavelength_capacity - kind: Number - optional: true - description: "Number of wavelengths supported on this degree" - - name: description - kind: Text - optional: true - description: "Additional degree details" - relationships: - - name: roadm - peer: DcimOpticalMultiplexerModule - cardinality: one - kind: Parent - optional: false - identifier: "optical_multiplexer_module__degrees" - description: "Parent ROADM module containing this degree" - - name: connected_fiber - peer: DcimFiberLink - cardinality: one - kind: Attribute - optional: true - identifier: "fiber_link__roadm_degree" - description: "Fiber link connected to this line port" - - name: channel_mappings - peer: DcimChannelMapping - cardinality: many - kind: Attribute - optional: true - identifier: "roadm_degree__channel_mappings" - description: "Channel mappings involving this degree" - - # ============================================================================ - # WSSConnect - Internal WSS Cross-Connect (Same ROADM) - # ============================================================================ - - name: WSSConnect # DcimWSSConnect - namespace: Dcim - inherit_from: - - DcimChannelMapping - include_in_menu: true - label: "WSS Connect" - description: "Internal WSS cross-connect between degrees on SAME ROADM device (0m distance)" - icon: mdi:swap-horizontal - display_label: computed_name__value - human_friendly_id: - - computed_name__value - uniqueness_constraints: - # FIXME: enforce per degrees ? - - [roadm, channel] - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - # TODO: should have the degrees, but need computed with Python for that - jinja2_template: "{{ roadm__computed_name__value }}-{{ channel__channel_name__value }}" - description: "Computed connection ID" - - name: description - kind: Text - optional: true - description: "Additional cross-connect details" - relationships: - - name: roadm - peer: DcimOpticalMultiplexerModule - cardinality: one - kind: Attribute - optional: false - identifier: "roadm_module__channel_mappings" - description: "Parent ROADM device" - # Note: channel and degrees relationships inherited from ChannelMapping generic - # TODO: Add validation to ensure both degrees have same parent as roadm - - # ============================================================================ - # CableMapping - Local Patch Cable Mapping (Different ROADMs, Same Site) - # ============================================================================ - - name: CableMapping # DcimCableMapping - namespace: Dcim - inherit_from: - - DcimChannelMapping - include_in_menu: true - label: "Cable Mapping" - description: "Local patch cable between degrees on DIFFERENT ROADMs (meters distance, same site)" - icon: mdi:cable-data - display_label: computed_name__value - human_friendly_id: - - computed_name__value - uniqueness_constraints: - - [cable, channel] - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: "{{ cable__label__value }}-{{ channel__channel_name__value }}" - description: "Computed mapping name (e.g., CABLE-123-CH58)" - - name: description - kind: Text - optional: true - description: "Additional cable mapping details" - relationships: - - name: cable - peer: DcimCable - cardinality: one - kind: Attribute - optional: false - identifier: "cable__channel_mappings" - description: "Physical patch cable connecting the degrees" - # Note: channel and degrees relationships inherited from ChannelMapping generic - # TODO: Add validation to ensure degrees are from different ROADMs - - # ============================================================================ - # OpticalAmplifierModule - EDFA/Raman/VGC Amplifier Module - # ============================================================================ - - name: OpticalAmplifierModule # DcimOpticalAmplifierModule - namespace: Dcim - include_in_menu: true - label: "Optical Amplifier Module" - description: "Optical signal amplifier module (EDFA, Raman, VGC) installed in chassis" - icon: mdi:amplifier - inherit_from: - - DcimGenericModule - display_label: computed_name__value - human_friendly_id: - - computed_name__value - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: "{{ module_bay__computed_name__value }}-AMP-{{ slot_number__value }}" - description: "Computed module name (e.g., dc1-leaf1 > slot 1-AMP-1/5)" - - name: slot_number - kind: Text - optional: false - description: "Slot number in chassis (e.g., '1/5', '1/6', '2/5')" - - name: amplifier_type - kind: Dropdown - optional: false - choices: - - name: edfa - label: "EDFA" - description: "Erbium-Doped Fiber Amplifier" - color: "#2196f3" - - name: raman - label: "Raman" - description: "Raman Amplifier (distributed)" - color: "#9c27b0" - - name: soa - label: "SOA" - description: "Semiconductor Optical Amplifier" - color: "#ff9800" - - name: vgc - label: "VGC" - description: "Variable Gain Controller" - color: "#4caf50" - - name: hybrid - label: "Hybrid" - description: "Hybrid amplifier (e.g., EDFA + Raman)" - color: "#607d8b" - description: "Type of optical amplifier" - - name: stage - kind: Dropdown - optional: false - choices: - - name: booster - label: "Booster" - description: "Power amplifier (transmit side)" - color: "#e74c3c" - - name: inline - label: "Inline" - description: "In-line amplifier (along fiber span)" - color: "#3498db" - - name: preamplifier - label: "Pre-Amplifier" - description: "Low-noise amplifier (receive side)" - color: "#2ecc71" - description: "Amplifier stage position" - - name: gain_db - kind: Text - optional: true - description: "Typical gain in dB (e.g., '20.0', '17.5')" - - name: max_output_power_dbm - kind: Text - optional: true - description: "Maximum output power in dBm (e.g., '17.0', '23.0')" - - name: noise_figure_db - kind: Text - optional: true - description: "Noise figure in dB (e.g., '5.5', '4.8')" - - name: operating_band - kind: Dropdown - optional: false - choices: - - name: c_band - label: "C-Band (1530-1565nm)" - description: "C-Band operation" - color: "#9933cc" - - name: l_band - label: "L-Band (1565-1625nm)" - description: "L-Band operation" - color: "#cc33cc" - - name: c_l_band - label: "C+L-Band" - description: "Dual-band operation" - color: "#ff33cc" - description: "Operating wavelength band" - relationships: - - name: amplified_link - peer: DcimFiberLink - cardinality: one - kind: Attribute - optional: true - identifier: "fiber_link__amplifier_modules" - description: "Fiber link being amplified (for inline amplifiers)" - - # ============================================================================ - # Service Layer - End-to-End Optical Services - # ============================================================================ - - # ============================================================================ - # OpticalService - End-to-End Customer Circuit - # ============================================================================ - - name: OpticalService # DcimOpticalService - namespace: Dcim - include_in_menu: true - label: "Optical Service" - description: "End-to-end optical transport service (customer circuit) spanning multiple sites" - icon: mdi:transit-connection-variant - display_label: service_name__value - human_friendly_id: - - service_id__value - uniqueness_constraints: - - [service_id__value] - order_by: - - service_id__value - attributes: - - name: service_id - kind: Text - optional: false - description: "Unique service identifier" - - name: service_name - kind: Text - optional: false - description: "Customer service name (e.g., 'ATC 10G - Albion to Arco #2')" - - name: service_type - kind: Dropdown - optional: false - choices: - - name: wavelength - label: "Wavelength Service" - description: "Dedicated wavelength end-to-end" - color: "#9c27b0" - - name: transport - label: "Transport Service" - description: "Layer 1 optical transport" - color: "#2196f3" - - name: ip_transit - label: "IP Transit" - description: "Layer 3 IP service over optical" - color: "#4caf50" - description: "Type of optical service" - - name: bandwidth - kind: Text - optional: false - description: "Service bandwidth (e.g., '10G', '100G', '400G')" - - name: status - kind: Dropdown - optional: false - default_value: "planned" - choices: - - name: planned - label: "Planned" - description: "Service planned but not provisioned" - color: "#95a5a6" - - name: provisioned - label: "Provisioned" - description: "Service configured but not active" - color: "#f5a623" - - name: active - label: "Active" - description: "Service in production" - color: "#00cc00" - - name: maintenance - label: "Maintenance" - description: "Service under maintenance" - color: "#ff9800" - - name: failed - label: "Failed" - description: "Service down" - color: "#cc0000" - description: "Service operational status" - - name: customer_circuit_id - kind: Text - optional: true - description: "Customer's circuit ID (e.g., '99/OKFS/000029//SYG')" - - name: provision_date - kind: DateTime - optional: true - description: "Date service was provisioned" - - name: description - kind: Text - optional: true - description: "Additional service details" - relationships: - - name: transponders - peer: DcimTransponderModule - cardinality: many - min_count: 2 - max_count: 2 - kind: Attribute - optional: false - identifier: "transponder_module__optical_services" - description: "Two transponders (endpoints) for this service (bidirectional)" - - name: channel - peer: DcimDWDMChannel - cardinality: one - kind: Attribute - optional: false - identifier: "dwdm_channel__services" - description: "DWDM channel used for this service" - - name: primary_path - peer: DcimOpticalPath - cardinality: one - kind: Component - optional: false - identifier: "optical_service__primary_path" - description: "Primary optical path for this service" - - name: backup_path - peer: DcimOpticalPath - cardinality: one - kind: Component - optional: true - identifier: "optical_service__backup_path" - description: "Backup/protection path for this service" - - # ============================================================================ - # OpticalPath - Network Path Through Multiple Sites - # ============================================================================ - - name: OpticalPath # DcimOpticalPath - namespace: Dcim - include_in_menu: true - label: "Optical Path" - description: "Ordered sequence of segments forming an optical path through the network" - icon: mdi:map-marker-path - display_label: name__value - human_friendly_id: - - name__value - attributes: - - name: name - kind: Text - optional: false - unique: true - description: "Path name (e.g., MS-W17D5-primary)" - - name: path_type - kind: Dropdown - optional: false - choices: - - name: primary - label: "Primary" - description: "Primary working path" - color: "#2196f3" - - name: backup - label: "Backup" - description: "Backup/protection path" - color: "#ff9800" - - name: express - label: "Express" - description: "Express bypass path" - color: "#9c27b0" - description: "Type of path (primary, backup, express)" - # TODO: Computed ? - - name: total_distance_km - kind: Text - optional: true - description: "Total path distance in kilometers" - # TODO: Computed ? - - name: total_loss_db - kind: Text - optional: true - description: "Total optical loss in dB" - # TODO: Computed ? - - name: hop_count - kind: Number - optional: true - description: "Number of ROADM hops in path" - # TODO: Replace with a status - - name: is_active - kind: Boolean - optional: false - default_value: true - description: "Whether this path is currently active" - relationships: - - name: segments - peer: DcimPathSegment - identifier: optical_path__segments - cardinality: many - kind: Component - optional: true - description: "Ordered segments forming this path" - - name: is_primary_of_service - peer: DcimOpticalService - identifier: optical_service__primary_path - cardinality: one - kind: Attribute - optional: true - description: "Optical service using this path" - - name: is_backup_of_service - peer: DcimOpticalService - identifier: optical_service__backup_path - cardinality: one - kind: Attribute - optional: true - description: "Optical service using this path as backup" - - # ============================================================================ - # PathSegment - One Hop in the Optical Path - # ============================================================================ - - name: PathSegment # DcimPathSegment - namespace: Dcim - include_in_menu: true - label: "Path Segment" - description: "One hop in optical path - references channel mapping (fiber, cable, or cross-connect)" - icon: mdi:ray-start-arrow - display_label: computed_name__value - human_friendly_id: - - computed_name__value - uniqueness_constraints: - - [path, segment_order__value] - attributes: - - name: computed_name - kind: Text - read_only: true - optional: false - computed_attribute: - kind: Jinja2 - jinja2_template: "{{ path__name__value }}-SEG{{ segment_order__value }}" - description: "Computed segment name (e.g., MS-W17D5-primary-SEG1)" - - name: segment_order - kind: Number - optional: false - description: "Order of this segment in the path (1, 2, 3...)" - - name: segment_type - kind: Dropdown - optional: false - choices: - - name: fiber - label: "Fiber" - description: "Segment traverses a long-haul fiber link" - color: "#2196f3" - - name: cross_connect - label: "Cross-Connect" - description: "Segment crosses through ROADM degrees (internal WSS)" - color: "#9c27b0" - - name: cable - label: "Cable" - description: "Segment traverses a local patch cable" - color: "#ff9800" - - name: add - label: "Add" - description: "Service added at this point (A-end)" - color: "#4caf50" - - name: drop - label: "Drop" - description: "Service dropped at this point (Z-end)" - color: "#ff5722" - description: "Type of segment" - - name: loss_db - kind: Text - optional: true - description: "Optical loss for this segment in dB" - relationships: - - name: path - peer: DcimOpticalPath - cardinality: one - kind: Parent - optional: false - identifier: optical_path__segments - description: "Parent path containing this segment" - - name: channel_mapping - peer: DcimChannelMapping - cardinality: one - kind: Attribute - on_delete: cascade - optional: true - identifier: "channel_mapping__path_segments" - description: "Channel mapping (WSS Connect, Cable Mapping, or Fiber Mapping)" - -extensions: - nodes: - - kind: DcimPhysicalDevice - relationships: - - name: optical_node - peer: DcimOpticalNode - kind: Generic - optional: true - cardinality: many - identifier: optical_nodes__physical_device diff --git a/extensions/optical_multiplexer/README.md b/extensions/optical_multiplexer/README.md deleted file mode 100644 index 11a07d92..00000000 --- a/extensions/optical_multiplexer/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# optical_multiplexer - -Please refer to the [reference page](https://docs.infrahub.app/schema-library/reference/optical_multiplexer) for the corresponding documentation. diff --git a/extensions/optical_multiplexer/optical_multiplexer.yml b/extensions/optical_multiplexer/optical_multiplexer.yml deleted file mode 100644 index f6e6311f..00000000 --- a/extensions/optical_multiplexer/optical_multiplexer.yml +++ /dev/null @@ -1,323 +0,0 @@ ---- -# yaml-language-server: $schema=https://schema.infrahub.app/infrahub/schema/latest.json -# --------------------------------------------------------------------------- -# This schema is a starting point for building with Infrahub, not a finished production model. -# Adapting it for your environment typically requires architectural review, see -# docs.infrahub.app, or reach out to OpsMill for help. -# --------------------------------------------------------------------------- -version: "1.0" - -generics: - - name: GenericOadmInterface - namespace: Dcim - description: Generic interface of an optical add-drop multiplexer, front or rear. - label: Optical Multiplexer Interfaces - icon: mdi:ethernet - include_in_menu: true - menu_placement: DcimOpticalMultiplexer - attributes: - - name: name - kind: Text - optional: false - order_weight: 1000 - - name: description - kind: Text - optional: true - order_weight: 1100 - # NOTE: this choice list is duplicated in extensions/patch_panel and the two must be - # kept in sync. Infrahub cannot share a choice list across extension files without a - # shared generic in a common ancestor, which is a base-tier change. - - name: connector_type - kind: Dropdown - choices: - - name: fc - label: FC - description: "Standardized fiber optic connector used primarily in datacom and telecom applications." - - name: lc - label: LC - description: "Compact fiber optic connector with a push-pull mechanism." - - name: lc_pc - label: LC/PC - description: "Polished LC connector providing physical contact (PC)." - - name: lc_upc - label: LC/UPC - description: "Ultra-Physical Contact (UPC) variant of the LC connector with enhanced polish." - - name: lc_apc - label: LC/APC - description: "Angled Physical Contact (APC) version of the LC connector with a slanted fiber end-face." - - name: lsh - label: LSH - description: "European fiber optic connector offering high durability." - - name: lsh_pc - label: LSH/PC - description: "Physical Contact version of LSH with standard polish." - - name: lsh_upc - label: LSH/UPC - description: "Ultra-Physical Contact variant of LSH, minimizing return loss with a superior polish." - - name: lsh_apc - label: LSH/APC - description: "Angled Physical Contact version of LSH, designed to reduce back reflections." - - name: lx_5 - label: LX.5 - description: "Miniaturized fiber optic connector similar to LC but with an additional shutter mechanism." - - name: lx_5_pc - label: LX.5/PC - description: "Physical Contact version of LX.5." - - name: lx_5_upc - label: LX.5/UPC - description: "Ultra-Physical Contact variant of LX.5." - - name: lx_5_apc - label: LX.5/APC - description: "Angled Physical Contact version of LX.5." - - name: mpo - label: MPO - description: "Multi-fiber Push-On connector typically used in data centers for high-speed applications." - - name: mtrj - label: MTRJ - description: "Male-to-female fiber optic connector with two fibers." - - name: sc - label: SC - description: "Square fiber optic connector with push-pull lock." - - name: sc_pc - label: SC/PC - description: "Physical Contact SC connector with a polished end-face." - - name: sc_upc - label: SC/UPC - description: "Ultra-Physical Contact variant of SC." - - name: sc_apc - label: SC/APC - description: "Angled Physical Contact version of SC." - - name: st - label: ST - description: "Bayonet-style fiber optic connector primarily used in industrial and military applications." - - name: cs - label: CS - description: "Compact connector with a high-density duplex configuration." - - name: sn - label: SN - description: "Small-form connector with dual fibers." - - name: sma_905 - label: SMA 905 - description: "Stainless steel fiber optic connector." - - name: sma_906 - label: SMA 906 - description: "Variant of SMA 905 with similar durability, frequently used in high-vibration settings." - - name: urm_p2 - label: URM-P2 - description: "Specialized fiber optic connector for industrial and harsh environments." - - name: urm_p4 - label: URM-P4 - description: "Similar to URM-P2 but designed for higher performance." - - name: urm_p8 - label: URM-P8 - description: "Enhanced version of URM connectors with higher protection." - - name: splice - label: Splice - description: "Permanent fiber connection method where two fiber ends are fused." - optional: false - order_weight: 1200 -nodes: - - name: OpticalMultiplexer - namespace: Dcim - description: >- - An OADM (Optical Add Drop Multiplexer) supporting various WDM - (Wavelength Division Multiplexing) technologies. - label: Optical Multiplexer - icon: mdi:transit-connection-variant - include_in_menu: true - inherit_from: - - DcimPhysicalDevice - human_friendly_id: - - name__value - order_by: - - name__value - display_label: name__value - attributes: - - name: name - kind: Text - unique: true - optional: false - order_weight: 1000 - - name: wdm_type - kind: Dropdown - default_value: dwdm - choices: - - name: cwdm - label: CWDM (Coarse Wavelength Division Multiplexing) - description: Supports multiple wavelengths for communication up to 70km. - color: "#0099cc" - - name: dwdm - label: DWDM (Dense Wavelength Division Multiplexing) - description: Supports dense wavelengths and amplification for long-distance communication. - color: "#9933cc" - optional: false - description: Type of WDM technology (e.g. CWDM, DWDM) - order_weight: 1300 - - name: description - kind: Text - optional: true - order_weight: 1100 - relationships: - - name: front_interfaces - peer: DcimOadmFrontInterface - kind: Component - cardinality: many - optional: true - identifier: optical_multiplexer__front_interfaces - order_weight: 1350 - - name: rear_interface - peer: DcimOadmRearInterface - kind: Component - cardinality: one - optional: true - identifier: optical_multiplexer__rear_interface - order_weight: 1450 - - - name: OadmFrontInterface - namespace: Dcim - description: Client-side interface of an optical add-drop multiplexer, carrying a single channel. - label: Optical Multiplexer Front Interfaces - include_in_menu: true - menu_placement: DcimGenericOadmInterface - inherit_from: - - DcimEndpoint - - DcimGenericOadmInterface - human_friendly_id: - - optical_multiplexer__name__value - - name__value - order_by: - - optical_multiplexer__name__value - - name__value - display_label: "{{ optical_multiplexer__name__value }} > {{ name__value }}" - uniqueness_constraints: - - [optical_multiplexer, name__value] - relationships: - - name: optical_multiplexer - peer: DcimOpticalMultiplexer - kind: Parent - cardinality: one - optional: false - identifier: optical_multiplexer__front_interfaces - order_weight: 900 - - name: channels - peer: DcimWdmChannel - kind: Attribute - cardinality: many - optional: true - identifier: oadm_interface__channels - order_weight: 1300 - - - name: OadmRearInterface - namespace: Dcim - description: Line-side interface of an optical add-drop multiplexer, carrying the multiplexed signal. - label: Optical Multiplexer Rear Interfaces - include_in_menu: true - menu_placement: DcimGenericOadmInterface - inherit_from: - - DcimEndpoint - - DcimGenericOadmInterface - human_friendly_id: - - optical_multiplexer__name__value - - name__value - order_by: - - optical_multiplexer__name__value - - name__value - display_label: "{{ optical_multiplexer__name__value }} > {{ name__value }}" - uniqueness_constraints: - - [optical_multiplexer, name__value] - relationships: - - name: optical_multiplexer - peer: DcimOpticalMultiplexer - kind: Parent - cardinality: one - optional: false - identifier: optical_multiplexer__rear_interface - order_weight: 900 - - - name: WdmChannel - namespace: Dcim - description: A WDM channel with its wavelength and frequency. - label: WDM Channel - icon: game-icons:laser-warning - include_in_menu: true - human_friendly_id: - - wdm_type__value - - channel__value - order_by: - - wdm_type__value - - channel__value - display_label: "{{ wdm_type__value }} Channel {{ channel__value }}" - uniqueness_constraints: - # Combination must be unique - - [frequency__value, wavelength__value, channel__value, wdm_type__value] - # For a given type channel number must be unique - - [channel__value, wdm_type__value] - # TODO: Maybe add some further validations - attributes: - - name: channel - kind: Number - optional: false - description: WDM channel number. - order_weight: 1000 - - name: wdm_type - kind: Dropdown - default_value: dwdm - choices: - - name: cwdm - label: CWDM (Coarse Wavelength Division Multiplexing) - description: Supports multiple wavelengths for communication up to 70km. - color: "#0099cc" - - name: dwdm - label: DWDM (Dense Wavelength Division Multiplexing) - description: Supports dense wavelengths and amplification for long-distance communication. - color: "#9933cc" - optional: false - description: Type of WDM technology (e.g. CWDM, DWDM) - order_weight: 1100 - - name: wavelength - kind: Text # Text until float possible in Number Kind - label: Wavelength (nm) - optional: false - description: Wavelength of the channel in nm. - order_weight: 1200 - - name: frequency - kind: Text # Text until float possible in Number Kind - label: Frequency (GHz) - optional: false - description: Frequency of the channel in GHz. - order_weight: 1300 - - - name: WdmTransceiver - namespace: Dcim - description: Transceiver tuned to a Wavelength Division Multiplexing channel. - label: WDM Transceiver - icon: mdi:laser-pointer - include_in_menu: true - menu_placement: DcimGenericTransceiver - inherit_from: - - DcimGenericTransceiver - attributes: - - name: wdm_type - kind: Dropdown - default_value: dwdm - choices: - - name: cwdm - label: CWDM (Coarse Wavelength Division Multiplexing) - description: Supports multiple wavelengths for communication up to 70km. - color: "#0099cc" - - name: dwdm - label: DWDM (Dense Wavelength Division Multiplexing) - description: Supports dense wavelengths and amplification for long-distance communication. - color: "#9933cc" - optional: false - description: Type of WDM technology (e.g. CWDM, DWDM) - order_weight: 1150 - relationships: - - name: wdm_channel - peer: DcimWdmChannel - label: WDM Channel - kind: Attribute - cardinality: one - optional: false - identifier: wdm_transceiver__channel - order_weight: 1250 diff --git a/extensions/patch_panel/patch_panel.yml b/extensions/patch_panel/patch_panel.yml index 7d263771..3580dfc8 100644 --- a/extensions/patch_panel/patch_panel.yml +++ b/extensions/patch_panel/patch_panel.yml @@ -24,9 +24,8 @@ generics: kind: Text optional: true order_weight: 1100 - # NOTE: this choice list is duplicated in extensions/optical_multiplexer and the two must be - # kept in sync. Infrahub cannot share a choice list across extension files without a - # shared generic in a common ancestor, which is a base-tier change. + # NOTE: this connector choice list is now unique to this extension. It was previously + # duplicated in extensions/optical_multiplexer, which has been removed. - name: connector_type kind: Dropdown choices: diff --git a/objects/extensions/optical_multiplexer/optical_multiplexer.yml b/objects/extensions/optical_multiplexer/optical_multiplexer.yml deleted file mode 100644 index 93d5098f..00000000 --- a/objects/extensions/optical_multiplexer/optical_multiplexer.yml +++ /dev/null @@ -1,170 +0,0 @@ -# --------------------------------------------------------------------------- -# Mock data for extensions/optical_multiplexer. -# Depends on: base, extensions/location_minimal (LocationSite: NYC1), -# extensions/rack (LocationRack: NYC1-RACK-01, DcimDevice: nyc1-rtr01), -# extensions/transceiver (DcimGenericTransceiver base + existing plugged/spare transceivers on -# GigabitEthernet0/0/0/0 and /1). -# -# DcimWdmChannel records are created first since both the OADM front -# interfaces and the WDM transceivers below reference them. DcimOpticalMultiplexer -# is created next, with its front/rear interfaces nested as Component -# children. A new interface (GigabitEthernet0/0/0/4) is added on nyc1-rtr01 -# for the plugged WDM transceiver demo, since GigabitEthernet0/0/0/0 through /3 are -# already occupied by other fixtures. -# --------------------------------------------------------------------------- ---- -apiVersion: infrahub.app/v1 -kind: Object -spec: - kind: DcimWdmChannel - data: - - channel: 1 - wdm_type: dwdm - wavelength: "1550.12" - frequency: "193.10" - - - channel: 2 - wdm_type: dwdm - wavelength: "1550.92" - frequency: "193.20" - - - channel: 20 - wdm_type: cwdm - wavelength: "1611.00" - frequency: "186.00" - ---- -apiVersion: infrahub.app/v1 -kind: Object -spec: - kind: DcimOpticalMultiplexer - data: - - name: NYC1-MUX01 - wdm_type: dwdm - description: Primary DWDM multiplexer for the NYC1 backbone ring. - position: 20 - rack_face: front - # `location` (from DcimPhysicalDevice) peer is LocationHosting, a - # Generic -- specify the concrete kind. Cardinality one, so `data` is - # a single mapping. This upserts against the existing rack. - location: - kind: LocationRack - data: - name: NYC1-RACK-01 - site: NYC1 - # front_interfaces is a Component relationship (cardinality many) -- - # nest the concrete kind with `data` as a list. The `optical_multiplexer` - # Parent relationship on each child is implied by this nesting. - front_interfaces: - kind: DcimOadmFrontInterface - data: - - name: "CH1" - connector_type: lc - # `channels` peer (DcimWdmChannel) is a CONCRETE kind whose - # human_friendly_id has two components ([wdm_type__value, - # channel__value]) and the relationship is cardinality many, so - # it's a plain list of HFID lists (same pattern as - # `bundle_members` in objects/extensions/lag/lag.yml). - channels: - - [dwdm, "1"] - - name: "CH2" - connector_type: lc - channels: - - [dwdm, "2"] - # rear_interface is a Component relationship of cardinality one -- - # nest the concrete kind with `data` as a single mapping. - rear_interface: - kind: DcimOadmRearInterface - data: - name: "COM" - connector_type: lc - - - name: NYC1-MUX02 - wdm_type: cwdm - description: Secondary CWDM multiplexer, spare capacity at NYC1. - position: 22 - rack_face: front - location: - kind: LocationRack - data: - name: NYC1-RACK-01 - site: NYC1 - front_interfaces: - kind: DcimOadmFrontInterface - data: - - name: "CH1" - connector_type: lc - channels: - - [cwdm, "20"] - rear_interface: - kind: DcimOadmRearInterface - data: - name: "COM" - connector_type: lc - ---- -apiVersion: infrahub.app/v1 -kind: Object -spec: - kind: DcimDevice - data: - # Upsert the existing fixture device to add a free physical interface - # for the plugged WDM transceiver below (GigabitEthernet0/0/0/0 through /3 are - # already in use by other fixtures). - - name: nyc1-rtr01 - location: - kind: LocationRack - data: - name: NYC1-RACK-01 - site: NYC1 - interfaces: - kind: InterfacePhysical - data: - - name: GigabitEthernet0/0/0/4 - role: core - status: active - ---- -apiVersion: infrahub.app/v1 -kind: Object -spec: - kind: DcimWdmTransceiver - data: - - transceiver_type: zr - form_factor: sfp_plus - status: plugged - wdm_type: dwdm - serial_number: "SFP-WDM-0001" - manufacturer: Cisco - # `interface` peer (InterfacePhysical) is a CONCRETE kind with a - # composite human_friendly_id ([device__name__value, name__value]). - interface: [nyc1-rtr01, GigabitEthernet0/0/0/4] - # `wdm_channel` peer (DcimWdmChannel) is a CONCRETE kind with a - # composite human_friendly_id ([wdm_type__value, channel__value]), - # cardinality one -- a plain list of the HFID parts. - wdm_channel: [dwdm, "1"] - - - transceiver_type: zr - form_factor: sfp_plus - status: spare - wdm_type: dwdm - serial_number: "SFP-WDM-0002" - manufacturer: Cisco - spare_location: - kind: LocationSite - data: - name: NYC1 - wdm_channel: [dwdm, "2"] - - - transceiver_type: zr - form_factor: qsfp28 - status: spare - wdm_type: cwdm - serial_number: "SFP-WDM-0003" - manufacturer: Cisco - spare_location: - kind: LocationRack - data: - name: NYC1-RACK-01 - site: NYC1 - wdm_channel: [cwdm, "20"] diff --git a/objects/extensions/routing_pim/routing_pim.yml b/objects/extensions/routing_pim/routing_pim.yml index 78838eb9..46d6b295 100644 --- a/objects/extensions/routing_pim/routing_pim.yml +++ b/objects/extensions/routing_pim/routing_pim.yml @@ -33,7 +33,7 @@ # [pim, interface]. # # GigabitEthernet0/0/0/0 through /4 are already used by other extensions' -# mock data (rack, cable, vrrp, transceiver, optical_multiplexer) -- new interfaces +# mock data (rack, cable, vrrp, transceiver) -- new interfaces # (GigabitEthernet0/0/0/5 and /0/0/0/6) are added below instead of # double-using an occupied one. They are created first, as a Component # relationship on the existing nyc1-rtr01 device (this upserts the device and diff --git a/objects/extensions/transceiver/transceiver.yml b/objects/extensions/transceiver/transceiver.yml index 8dbb2b21..d400a3c0 100644 --- a/objects/extensions/transceiver/transceiver.yml +++ b/objects/extensions/transceiver/transceiver.yml @@ -4,9 +4,6 @@ # extensions/rack (LocationRack: NYC1-RACK-01, DcimDevice: nyc1-rtr01 with # interface GigabitEthernet0/0/0/0), extensions/organization (OrganizationManufacturer: # Cisco, Dell). -# -# extensions/optical_multiplexer builds on top of extensions/transceiver in a later wave -- this file -# stays self-contained and doesn't assume dwdm data exists. # --------------------------------------------------------------------------- --- apiVersion: infrahub.app/v1 From 7fa7f820c4d8cc28c8880c7402092b244418d9a8 Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 10:33:14 +0200 Subject: [PATCH 02/31] feat(otn): add OTN schema extension --- .metadata.yml | 9 + extensions/otn/README.md | 3 + extensions/otn/otn.yml | 4335 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 4347 insertions(+) create mode 100644 extensions/otn/README.md create mode 100644 extensions/otn/otn.yml diff --git a/.metadata.yml b/.metadata.yml index 29660995..fd2f9140 100644 --- a/.metadata.yml +++ b/.metadata.yml @@ -221,6 +221,15 @@ extensions/mlag: Not covered yet: the layer 3 overlay for MLAG interfaces (loopback, peer address ...) and surfacing MLAG interfaces on each device in the domain. name: MLAG +extensions/otn: + dependencies: + - base + - extensions/location_site + description: | + Optical transport network schemas covering the physical plant, the + wavelength catalog, optical devices and their ports, pluggable optics, + carriers and end-to-end services. + name: OTN extensions/patch_panel: dependencies: - base diff --git a/extensions/otn/README.md b/extensions/otn/README.md new file mode 100644 index 00000000..f665a17a --- /dev/null +++ b/extensions/otn/README.md @@ -0,0 +1,3 @@ +# otn + +Please refer to the [reference page](https://docs.infrahub.app/schema-library/reference/otn) for the corresponding documentation. diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml new file mode 100644 index 00000000..4aeed478 --- /dev/null +++ b/extensions/otn/otn.yml @@ -0,0 +1,4335 @@ +--- +# yaml-language-server: $schema=https://schema.infrahub.app/infrahub/schema/latest.json +# --------------------------------------------------------------------------- +# This schema is a starting point for building with Infrahub, not a finished production model. +# Adapting it for your environment typically requires architectural review, see +# docs.infrahub.app, or reach out to OpsMill for help. +# --------------------------------------------------------------------------- +# Optical transport network. Ported from opsmill/infrahub-demo-otn at e98be9b. +# Devices here are optical roles; OtnGenericDevice.dcim_device links one to its +# DcimPhysicalDevice record. See the reference page for what is out of scope. +# --------------------------------------------------------------------------- +version: "1.0" + +generics: + # ---- from schemas/otn_base.yml ---- + - name: GenericPort + namespace: Otn + description: Any port on any OTN device. The connection surface of the network. + label: Port + icon: mdi:ethernet + include_in_menu: false + human_friendly_id: + - device__name__value + - name__value + order_by: + - name__value + display_label: "{{ device__name__value }} {{ name__value }}" + uniqueness_constraints: + - ["device", "name__value"] + attributes: + - name: name + kind: Text + parameters: + max_length: 64 + regex: "^[A-Za-z0-9._/-]+$" + optional: false + description: Port identifier as the vendor labels it, such as 1/1/1. + order_weight: 1000 + - name: role + kind: Dropdown + default_value: spare + choices: + - name: client + label: Client + color: "#4caf50" + - name: line + label: Line + color: "#2196f3" + - name: add_drop + label: Add/drop + color: "#9c27b0" + - name: degree + label: Degree + color: "#673ab7" + - name: booster + label: Booster + color: "#ff9800" + - name: preamp + label: Pre-amplifier + color: "#ffc107" + - name: tributary + label: Tributary + color: "#795548" + - name: spare + label: Spare + color: "#9e9e9e" + - name: monitor + label: Monitor + description: A monitoring interface, not a traffic-carrying port. + color: "#607d8b" + optional: false + order_weight: 1100 + - name: enabled + kind: Boolean + default_value: true + optional: false + order_weight: 1200 + - name: admin_state + kind: Dropdown + default_value: up + choices: + - name: up + label: Up + color: "#4caf50" + - name: down + label: Down + color: "#f44336" + - name: testing + label: Testing + color: "#ff9800" + optional: false + description: RFC 2863 ifAdminStatus. What the operator asked for. + order_weight: 1300 + - name: oper_state + kind: Dropdown + default_value: down + choices: + - name: up + label: Up + color: "#4caf50" + - name: down + label: Down + color: "#f44336" + - name: testing + label: Testing + color: "#ff9800" + - name: dormant + label: Dormant + color: "#2196f3" + - name: unknown + label: Unknown + color: "#9e9e9e" + optional: false + description: RFC 2863 ifOperStatus subset. What the port actually does. + order_weight: 1400 + relationships: + - name: device + peer: OtnGenericDevice + kind: Parent + cardinality: one + optional: false + identifier: otn_device__ports + order_weight: 800 + # Declared once. The relationship is symmetric and self-referencing, so + # both ends are the same edge; the explicit identifier is what stops + # Infrahub deriving a different string per side and splitting it into two + # phantom one-way links. Immutable after the first load: changing the + # identifier would orphan every connection already recorded. + - name: connected_to + peer: OtnGenericPort + kind: Attribute + cardinality: one + optional: true + identifier: otn_port__connected_to + on_delete: no-action + order_weight: 900 + + - name: OpticalElement + namespace: Otn + description: Anything light passes through and loses power in. + label: Optical element + icon: mdi:blur-linear + include_in_menu: false + # No order_by and no human_friendly_id. `order_by: [name__value]` is + # rejected at load with "OtnOpticalElement.order_by: attribute 'name' not + # defined on this schema": the key resolves against the generic's own + # attributes, not against what its members inherit, and this generic + # declares none. So a hop's element is written by UUID and no object file + # can name one. Every member kind carries its own order_by. + attributes: + - name: insertion_loss_mdb + kind: Number + default_value: 0 + parameters: + min_value: 0 + max_value: 60000 + optional: false + description: Insertion loss in millidecibels, 0 to 60 dB. No Float kind exists. + order_weight: 1500 + - name: insertion_loss_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if insertion_loss_mdb__value is not none %}{{ insertion_loss_mdb__value / 1000 }} dB{% endif %} + optional: true + description: Insertion loss in dB. Read-only, derived from insertion_loss_mdb. + order_weight: 1510 + - name: vendor + kind: Text + parameters: + max_length: 64 + optional: true + order_weight: 1600 + - name: model + kind: Text + parameters: + max_length: 64 + optional: true + order_weight: 1700 + # Adding a choice here is not enough. Every kind that overrides this + # attribute restates the whole list, and the server accepts a restatement + # that is one choice short without a word; the divergence surfaces only + # when an object writes the missing value. The tenth choice, `odu_switch`, + # cost nine blocks and the list now lives in eleven: this one, the nine + # overrides in otn_devices.yml, and the span's in otn_plant.yml. The pinned + # list in `tests/unit/test_schema_contract.py` is the only thing that will + # tell you a block was missed. + - name: element_class + kind: Dropdown + choices: + - name: transponder + label: Transponder + color: "#2196f3" + - name: roadm + label: ROADM + color: "#9c27b0" + - name: amplifier + label: Amplifier + color: "#ff9800" + - name: mux_demux + label: Mux/demux + color: "#00bcd4" + - name: patch_panel + label: Patch panel + color: "#9e9e9e" + - name: fiber_span + label: Fiber span + color: "#4caf50" + - name: splitter + label: Splitter + color: "#8bc34a" + - name: attenuator + label: Attenuator + color: "#795548" + - name: raman_pump + label: Raman pump + color: "#e91e63" + - name: odu_switch + label: ODU switch + color: "#673ab7" + optional: false + description: What the optical budget engine branches on. + order_weight: 1800 + + - name: GenericDevice + namespace: Otn + description: Anything racked at a site. + label: Device + icon: mdi:server + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: "^[A-Za-z0-9._-]+$" + optional: false + order_weight: 1000 + - name: status + kind: Dropdown + default_value: active + choices: + - name: planned + label: Planned + color: "#2196f3" + - name: provisioning + label: Provisioning + color: "#ff9800" + - name: active + label: Active + color: "#4caf50" + - name: maintenance + label: Maintenance + color: "#ffc107" + - name: decommissioned + label: Decommissioned + color: "#9e9e9e" + optional: false + order_weight: 1100 + - name: role + kind: Dropdown + choices: + - name: core + label: Core + color: "#3f51b5" + - name: edge + label: Edge + color: "#2196f3" + - name: access + label: Access + color: "#00bcd4" + - name: passive + label: Passive + color: "#9e9e9e" + optional: false + order_weight: 1200 + relationships: + # Not a Parent: Parent would force optional: false, and a device has to be + # creatable before its site record exists. + - name: site + peer: OtnSite + kind: Attribute + cardinality: one + optional: true + identifier: otn_site__devices + on_delete: no-action + order_weight: 800 + - name: ports + peer: OtnGenericPort + kind: Component + cardinality: many + optional: true + identifier: otn_device__ports + on_delete: cascade + order_weight: 900 + + - name: OpticalPort + namespace: Otn + description: Optical-specific port properties. + label: Optical port + icon: mdi:lightbulb-on-outline + include_in_menu: false + attributes: + # Optional because a grey router port is not on the C-band grid. The + # bounds are the two endpoints of the 96-channel 50 GHz grid in units.py, + # asserted equal by tests/unit/test_schema_contract.py. + - name: center_frequency_mhz + kind: Number + parameters: + min_value: 191350000 + max_value: 196100000 + optional: true + description: ITU-T G.694.1 centre frequency in MHz. No Float kind exists. + order_weight: 1600 + - name: center_frequency_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if center_frequency_mhz__value is not none %}{{ center_frequency_mhz__value / 1000000 }} THz{% endif %} + optional: true + description: Centre frequency in THz. Empty on a grey port, which has none. + order_weight: 1610 + - name: tx_power_mdbm + kind: Number + parameters: + min_value: -30000 + max_value: 30000 + optional: true + description: Transmit power in milli-dBm, -30 to +30 dBm. + order_weight: 1700 + - name: tx_power_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if tx_power_mdbm__value is not none %}{{ tx_power_mdbm__value / 1000 }} dBm{% endif %} + optional: true + description: Transmit power in dBm. Read-only, derived from tx_power_mdbm. + order_weight: 1710 + - name: rx_sensitivity_mdbm + kind: Number + parameters: + min_value: -40000 + max_value: 10000 + optional: true + description: Receiver sensitivity in milli-dBm, -40 to +10 dBm. + order_weight: 1800 + - name: rx_sensitivity_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if rx_sensitivity_mdbm__value is not none %}{{ rx_sensitivity_mdbm__value / 1000 }} dBm{% endif %} + optional: true + description: Receiver sensitivity in dBm. Derived from rx_sensitivity_mdbm. + order_weight: 1810 + # A Dropdown rather than an enum, so a connector carries a label, a colour + # and a sentence. The six original names are unchanged, so no loaded value + # is rewritten; MU, CS and splice are new. + # + # Changing the kind on this generic alone is refused with + # "connector_type inherited from OtnOpticalPort must be the same kind + # ["Dropdown", "Text"]", because the loaded schema has materialised the Text + # attribute onto every optical port kind. All six kinds that inherit this + # generic restate the list in otn_ports.yml, and a restatement one choice + # short is accepted in silence. + # + # OtnCopperPort keeps its own connector_type as Text; no kind inherits both + # port generics. + - name: connector_type + kind: Dropdown + choices: + - name: LC + label: LC + description: Small-form duplex latch. What transponder and pluggable optics take. + color: "#2196f3" + - name: SC + label: SC + description: Push-pull square ferrule, common on older distribution frames. + color: "#4caf50" + - name: MPO-12 + label: MPO-12 + description: Twelve-fibre ribbon ferrule for parallel optics. + color: "#9c27b0" + - name: MPO-16 + label: MPO-16 + description: Sixteen-fibre ribbon ferrule for 400G parallel optics. + color: "#673ab7" + - name: FC + label: FC + description: Threaded ferrule. Still found on test sets and metro equipment. + color: "#ff9800" + - name: E2000 + label: E2000 + description: Shuttered push-pull connector, widespread on European carrier plant. + color: "#00bcd4" + - name: MU + label: MU + description: Miniature unibody connector, half the footprint of an SC. + color: "#8bc34a" + - name: CS + label: CS + description: Narrow-pitch duplex connector, used for QSFP-DD breakout. + color: "#e91e63" + - name: splice + label: Splice + description: Fusion splice rather than a mated pair. No connector loss and no polish. + color: "#9e9e9e" + optional: true + order_weight: 1900 + # Return loss is a polish property, not a connector property: an LC comes + # in all three. Null on every port loaded before this attribute existed, + # which is what keeps the change non-destructive. + - name: polish + kind: Dropdown + choices: + - name: UPC + label: UPC + description: Ultra physical contact. Blue housing, about 50 dB return loss. + color: "#2196f3" + - name: APC + label: APC + description: Angled physical contact. Green housing, about 65 dB return loss. + color: "#4caf50" + - name: PC + label: PC + description: Physical contact. The oldest polish, about 35 dB return loss. + color: "#ff9800" + - name: none + label: None + description: No polished endface, such as a fusion splice. + color: "#9e9e9e" + optional: true + description: Endface polish. Mating an APC to a UPC costs about 0.5 dB and reflects. + order_weight: 1910 + + - name: CopperPort + namespace: Otn + description: Electrical port properties for G.703 tributaries. + label: Copper port + icon: mdi:cable-data + include_in_menu: false + attributes: + # kbps, not Mbps. E1 is 2.048 Mbps and T1 is 1.544 Mbps; as integer Mbps + # both round to 2, which would make the two signals this generic exists to + # carry indistinguishable. + - name: speed_kbps + kind: Number + default_value: 2048 + parameters: + min_value: 64 + max_value: 400000000 + optional: false + description: Line rate in kbps. E1 is 2048, T1 is 1544, STM-1 is 155520. + order_weight: 1600 + - name: impedance_ohm + kind: Number + default_value: 120 + parameters: + min_value: 50 + max_value: 600 + optional: false + description: Nominal impedance in ohms. 120 for E1 pair, 75 for coax. + order_weight: 1700 + - name: connector_type + kind: Text + default_value: RJ48 + enum: + - BNC + - RJ48 + - RJ45 + optional: false + order_weight: 1900 + + # ---- from schemas/otn_ports.yml ---- + - name: Monitor + namespace: Otn + description: The one fact every monitoring interface carries, whatever it measures. + label: Monitor + icon: mdi:gauge + include_in_menu: false + attributes: + # A reading with no timestamp is not a reading. This is a last known + # value, not a telemetry feed; the age is what makes drift meaningful. + - name: measured_at + kind: DateTime + optional: false + description: When this reading was taken. + order_weight: 1110 + + - name: ChannelMonitor + namespace: Otn + description: What a device that sees a band of channels reports about that band. + label: Channel monitor + icon: mdi:chart-histogram + include_in_menu: false + # A ROADM degree and a multiplexer report the same two numbers, so the two + # are declared once here rather than twice. The kinds stay distinct, + # because a degree and a multiplexer are different equipment and a query + # should be able to say which it means. It also leaves room for the two to + # diverge without a migration on loaded data. + # + # This generic does not inherit OtnMonitor. Generics do not inherit + # generics; the concrete kinds take both. + attributes: + - name: total_power_mdbm + kind: Number + parameters: + min_value: -40000 + max_value: 30000 + optional: false + description: Total power across all channels present, in millidecibel-milliwatts. + order_weight: 1300 + - name: channel_count + kind: Number + parameters: + min_value: 0 + max_value: 96 + optional: false + description: How many channels the monitor currently sees. + order_weight: 1310 + +nodes: + # ---- from schemas/otn_ports.yml ---- + - name: RouterPort + namespace: Otn + description: Grey optics on an IP router. Not on the C-band grid. + label: Router port + icon: mdi:ethernet + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnOpticalPort + attributes: + - name: connector_type + kind: Dropdown + choices: + - name: LC + label: LC + description: Small-form duplex latch. What transponder and pluggable optics take. + color: "#2196f3" + - name: SC + label: SC + description: Push-pull square ferrule, common on older distribution frames. + color: "#4caf50" + - name: MPO-12 + label: MPO-12 + description: Twelve-fibre ribbon ferrule for parallel optics. + color: "#9c27b0" + - name: MPO-16 + label: MPO-16 + description: Sixteen-fibre ribbon ferrule for 400G parallel optics. + color: "#673ab7" + - name: FC + label: FC + description: Threaded ferrule. Still found on test sets and metro equipment. + color: "#ff9800" + - name: E2000 + label: E2000 + description: Shuttered push-pull connector, widespread on European carrier plant. + color: "#00bcd4" + - name: MU + label: MU + description: Miniature unibody connector, half the footprint of an SC. + color: "#8bc34a" + - name: CS + label: CS + description: Narrow-pitch duplex connector, used for QSFP-DD breakout. + color: "#e91e63" + - name: splice + label: Splice + description: Fusion splice rather than a mated pair. No connector loss and no polish. + color: "#9e9e9e" + optional: true + order_weight: 1900 + relationships: + # The module fitted in this port, and the inverse of OtnTransceiver.port. + # Both ends are cardinality one, so the edge is one to one: the server + # refuses the second write with "has 2 peers for + # otn_optical_port__transceiver, maximum of 1 allowed". That is the + # duplicate rule, carried by the schema rather than by a check. + # + # Declared on the three concrete kinds that have a cage and never on the + # OtnOpticalPort generic, which would hand the field to all eight optical + # port kinds including the five that hold no module. + # + # Optional on both sides, so a spare, an RMA and a decommissioned unit stay + # modellable. + - name: transceiver + peer: OtnTransceiver + label: Module fitted in this port + kind: Attribute + cardinality: one + optional: true + identifier: otn_optical_port__transceiver + on_delete: no-action + order_weight: 960 + + - name: ClientPort + namespace: Otn + description: Transponder client side. Where grey light arrives. + label: Client port + icon: mdi:lan-connect + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnOpticalPort + attributes: + - name: connector_type + kind: Dropdown + choices: + - name: LC + label: LC + description: Small-form duplex latch. What transponder and pluggable optics take. + color: "#2196f3" + - name: SC + label: SC + description: Push-pull square ferrule, common on older distribution frames. + color: "#4caf50" + - name: MPO-12 + label: MPO-12 + description: Twelve-fibre ribbon ferrule for parallel optics. + color: "#9c27b0" + - name: MPO-16 + label: MPO-16 + description: Sixteen-fibre ribbon ferrule for 400G parallel optics. + color: "#673ab7" + - name: FC + label: FC + description: Threaded ferrule. Still found on test sets and metro equipment. + color: "#ff9800" + - name: E2000 + label: E2000 + description: Shuttered push-pull connector, widespread on European carrier plant. + color: "#00bcd4" + - name: MU + label: MU + description: Miniature unibody connector, half the footprint of an SC. + color: "#8bc34a" + - name: CS + label: CS + description: Narrow-pitch duplex connector, used for QSFP-DD breakout. + color: "#e91e63" + - name: splice + label: Splice + description: Fusion splice rather than a mated pair. No connector loss and no polish. + color: "#9e9e9e" + optional: true + order_weight: 1900 + relationships: + # The one-to-one edge OtnRouterPort.transceiver documents, here on the kind + # that takes a grey pluggable on the transponder side. + - name: transceiver + peer: OtnTransceiver + label: Module fitted in this port + kind: Attribute + cardinality: one + optional: true + identifier: otn_optical_port__transceiver + on_delete: no-action + order_weight: 960 + + - name: LinePort + namespace: Otn + description: Transponder DWDM line side. Where coloured light leaves. + label: Line port + icon: mdi:transit-connection + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnOpticalPort + attributes: + - name: connector_type + kind: Dropdown + choices: + - name: LC + label: LC + description: Small-form duplex latch. What transponder and pluggable optics take. + color: "#2196f3" + - name: SC + label: SC + description: Push-pull square ferrule, common on older distribution frames. + color: "#4caf50" + - name: MPO-12 + label: MPO-12 + description: Twelve-fibre ribbon ferrule for parallel optics. + color: "#9c27b0" + - name: MPO-16 + label: MPO-16 + description: Sixteen-fibre ribbon ferrule for 400G parallel optics. + color: "#673ab7" + - name: FC + label: FC + description: Threaded ferrule. Still found on test sets and metro equipment. + color: "#ff9800" + - name: E2000 + label: E2000 + description: Shuttered push-pull connector, widespread on European carrier plant. + color: "#00bcd4" + - name: MU + label: MU + description: Miniature unibody connector, half the footprint of an SC. + color: "#8bc34a" + - name: CS + label: CS + description: Narrow-pitch duplex connector, used for QSFP-DD breakout. + color: "#e91e63" + - name: splice + label: Splice + description: Fusion splice rather than a mated pair. No connector loss and no polish. + color: "#9e9e9e" + optional: true + order_weight: 1900 + relationships: + # The wavelength this port terminates, and the deletion boundary. + # + # Deleting a transponder already deletes this port: OtnGenericDevice.ports + # is kind: Component with on_delete: cascade, and a port has no meaning + # apart from the device it sits in. The deletion stops here. + # + # A carrier is not a component of a line port. It is a spectrum allocation + # on a route, holding a channel, a mode, its sections, its containers and + # possibly ODU switches, and it exists in the plan whether or not a box is + # currently lighting it. Cascading from this port would reach the carrier's + # containers and the services groomed into them, so pulling one transponder + # would destroy customer service records. It would also delete the + # wavelength out from under the line port at the far end, which is still + # installed: a carrier is terminated at both of its ends. + # + # Same policy as OtnService.diversity_group and OtnService.containers, for + # the same reason those two state. + # + # Light has direction, and that is not an argument for cascade here. A line + # port is a transceiver: every one of them carries both tx_power_mdbm and + # rx_sensitivity_mdbm, so there is no single transmitting end to own the + # wavelength. Cascade on a cardinality-one relationship fires from either + # port, so it would give two owners where the intuition asked for one. + # OtnOpticalCarrier holds no direction at all, deliberately: where this + # model needs direction it makes two objects, the way an amplifier hut + # does, and the budget engine evaluates a carrier both ways and takes the + # worse. Direction lives on the plant and on the path traversal, never on + # the wavelength. + # + # The model also already says "the light stopped" with status, which is + # planned, active or decommissioned. A planned carrier has no light and + # exists anyway. This kind is a spectrum allocation on a route, not the + # photons on it, and deleting a box must not silently release spectrum + # that channel_collision.py exists to police. + # + # There is a demo argument too, and it is the stronger one. A cascade makes + # the fault disappear. no-action leaves a lit carrier with nothing + # terminating it, which is visible and checkable, the shape + # monitor_completeness already works in. + - name: carrier + peer: OtnOpticalCarrier + label: Wavelength this port terminates + kind: Attribute + cardinality: one + optional: true + identifier: otn_carrier__line_ports + on_delete: no-action + order_weight: 850 + # The one-to-one edge OtnRouterPort.transceiver documents, here on the kind + # that takes a coloured pluggable on the line side. + - name: transceiver + peer: OtnTransceiver + label: Module fitted in this port + kind: Attribute + cardinality: one + optional: true + identifier: otn_optical_port__transceiver + on_delete: no-action + order_weight: 960 + + - name: RoadmAddDropPort + namespace: Otn + description: Local add and drop on a ROADM. + label: ROADM add/drop port + icon: mdi:call-split + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnOpticalPort + attributes: + - name: connector_type + kind: Dropdown + choices: + - name: LC + label: LC + description: Small-form duplex latch. What transponder and pluggable optics take. + color: "#2196f3" + - name: SC + label: SC + description: Push-pull square ferrule, common on older distribution frames. + color: "#4caf50" + - name: MPO-12 + label: MPO-12 + description: Twelve-fibre ribbon ferrule for parallel optics. + color: "#9c27b0" + - name: MPO-16 + label: MPO-16 + description: Sixteen-fibre ribbon ferrule for 400G parallel optics. + color: "#673ab7" + - name: FC + label: FC + description: Threaded ferrule. Still found on test sets and metro equipment. + color: "#ff9800" + - name: E2000 + label: E2000 + description: Shuttered push-pull connector, widespread on European carrier plant. + color: "#00bcd4" + - name: MU + label: MU + description: Miniature unibody connector, half the footprint of an SC. + color: "#8bc34a" + - name: CS + label: CS + description: Narrow-pitch duplex connector, used for QSFP-DD breakout. + color: "#e91e63" + - name: splice + label: Splice + description: Fusion splice rather than a mated pair. No connector loss and no polish. + color: "#9e9e9e" + optional: true + order_weight: 1900 + + - name: RoadmDegreePort + namespace: Otn + description: Line-facing degree on a ROADM. One per direction. + label: ROADM degree port + icon: mdi:compass-outline + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnOpticalPort + attributes: + - name: connector_type + kind: Dropdown + choices: + - name: LC + label: LC + description: Small-form duplex latch. What transponder and pluggable optics take. + color: "#2196f3" + - name: SC + label: SC + description: Push-pull square ferrule, common on older distribution frames. + color: "#4caf50" + - name: MPO-12 + label: MPO-12 + description: Twelve-fibre ribbon ferrule for parallel optics. + color: "#9c27b0" + - name: MPO-16 + label: MPO-16 + description: Sixteen-fibre ribbon ferrule for 400G parallel optics. + color: "#673ab7" + - name: FC + label: FC + description: Threaded ferrule. Still found on test sets and metro equipment. + color: "#ff9800" + - name: E2000 + label: E2000 + description: Shuttered push-pull connector, widespread on European carrier plant. + color: "#00bcd4" + - name: MU + label: MU + description: Miniature unibody connector, half the footprint of an SC. + color: "#8bc34a" + - name: CS + label: CS + description: Narrow-pitch duplex connector, used for QSFP-DD breakout. + color: "#e91e63" + - name: splice + label: Splice + description: Fusion splice rather than a mated pair. No connector loss and no polish. + color: "#9e9e9e" + optional: true + order_weight: 1900 + + - name: AmplifierPort + namespace: Otn + description: Amplifier input or output. + label: Amplifier port + icon: mdi:amplifier + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnOpticalPort + attributes: + - name: connector_type + kind: Dropdown + choices: + - name: LC + label: LC + description: Small-form duplex latch. What transponder and pluggable optics take. + color: "#2196f3" + - name: SC + label: SC + description: Push-pull square ferrule, common on older distribution frames. + color: "#4caf50" + - name: MPO-12 + label: MPO-12 + description: Twelve-fibre ribbon ferrule for parallel optics. + color: "#9c27b0" + - name: MPO-16 + label: MPO-16 + description: Sixteen-fibre ribbon ferrule for 400G parallel optics. + color: "#673ab7" + - name: FC + label: FC + description: Threaded ferrule. Still found on test sets and metro equipment. + color: "#ff9800" + - name: E2000 + label: E2000 + description: Shuttered push-pull connector, widespread on European carrier plant. + color: "#00bcd4" + - name: MU + label: MU + description: Miniature unibody connector, half the footprint of an SC. + color: "#8bc34a" + - name: CS + label: CS + description: Narrow-pitch duplex connector, used for QSFP-DD breakout. + color: "#e91e63" + - name: splice + label: Splice + description: Fusion splice rather than a mated pair. No connector loss and no polish. + color: "#9e9e9e" + optional: true + order_weight: 1900 + + # One multiplexer channel and the common side it shares. Neither declares an + # insertion loss: the loss belongs to the device and the budget engine reads it + # from the path hop's element, so a second per-port figure would double count. + - name: MuxClientPort + namespace: Otn + description: One channel of a multiplexer. The side facing the transponder or router that lights it. + label: Mux client port + icon: mdi:import + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnOpticalPort + # Two optional edges, not one mandatory edge to a shared channel generic. The + # dense and coarse grid kinds were kept apart, and Infrahub has no + # cross-relationship constraint, so the schema cannot say "exactly one of + # these two". mux_channel_binding owns the neither case and the both case. + # + # Neither cascades: deleting a channel must not delete the port that named it. + relationships: + - name: dwdm_channel + peer: OtnFrequencyGrid + label: Dense channel this port carries + kind: Attribute + cardinality: one + optional: true + identifier: otn_frequency_grid__mux_client_ports + on_delete: no-action + order_weight: 850 + - name: cwdm_channel + peer: OtnCwdmChannel + label: Coarse wavelength this port carries + kind: Attribute + cardinality: one + optional: true + identifier: otn_cwdm_channel__mux_client_ports + on_delete: no-action + order_weight: 860 + + # It binds no channel because it carries every channel the device lights. + - name: MuxLinePort + namespace: Otn + description: The common side of a multiplexer, where the whole band leaves. + label: Mux line port + icon: mdi:export + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnOpticalPort + + - name: TributaryPort + namespace: Otn + description: E1 or T1 G.703 electrical tributary. The only copper port here. + label: Tributary port + icon: mdi:cable-data + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnCopperPort + + # The five monitors. They declare readings, because what a monitor measures is + # what distinguishes it. Eleven of the fourteen readings belong to exactly one + # family, so a single kind with a discriminator could not say which readings a + # monitor must carry and which it cannot take. Five kinds can, and the schema + # then refuses both halves: a missing reading, because the attribute is + # mandatory, and an impossible one, because the kind has no field for it. + - name: AmplifierMonitor + # Nothing here constrains which device kind a monitor may hang off, and that + # is measured rather than overlooked. Infrahub 1.11.0 refuses to let a + # concrete kind narrow the peer of a relationship it inherits, from either + # end, at schema check and before any load. OtnGenericPort.device peers + # OtnGenericDevice, so a receiver monitor on an amplifier is writable. The + # remaining option was a Python check, which is the layer these five kinds + # exist to leave, so the gap is accepted and stated instead. The generator + # that writes the monitors is the only thing that ever creates one. + namespace: Otn + description: The last reading taken from an amplifier's monitoring interface. + label: Amplifier monitor + icon: mdi:gauge + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnMonitor + order_by: + - name__value + attributes: + - name: input_power_mdbm + kind: Number + parameters: + min_value: -40000 + max_value: 30000 + optional: false + description: Total input power in millidecibel-milliwatts. -3.5 dBm is -3500. + order_weight: 1200 + - name: input_power_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if input_power_mdbm__value is not none %}{{ input_power_mdbm__value / 1000 }} dBm{% endif %} + optional: true + description: Input power in dBm. Read-only, derived from input_power_mdbm. + order_weight: 1210 + - name: output_power_mdbm + kind: Number + parameters: + min_value: -40000 + max_value: 30000 + optional: false + description: Total output power in millidecibel-milliwatts. + order_weight: 1220 + - name: output_power_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if output_power_mdbm__value is not none %}{{ output_power_mdbm__value / 1000 }} dBm{% endif %} + optional: true + description: Output power in dBm. Read-only, derived from output_power_mdbm. + order_weight: 1230 + # measured_gain_mdb is declared here and on OtnRamanMonitor, with + # identical bounds and an identical template. It cannot live on + # OtnMonitor, because that would put a gain field on the channel and + # receiver monitors, which is the failure these five kinds remove. + - name: measured_gain_mdb + kind: Number + parameters: + min_value: 0 + max_value: 40000 + optional: false + description: Gain the amplifier is actually delivering, in millidecibels. + order_weight: 1240 + - name: measured_gain_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if measured_gain_mdb__value is not none %}{{ measured_gain_mdb__value / 1000 }} dB{% endif %} + optional: true + description: Measured gain in dB. Read-only, derived from measured_gain_mdb. + order_weight: 1250 + - name: tilt_mdb + kind: Number + parameters: + min_value: -5000 + max_value: 5000 + optional: false + description: Gain tilt across the band in millidecibels. Negative tilts the red end down. + order_weight: 1260 + + # No attributes of its own. Both readings come from OtnChannelMonitor, which + # OtnMuxDemuxMonitor takes too, so the pair is declared once. + - name: RoadmDegreeMonitor + namespace: Otn + description: The last reading taken from a ROADM degree's monitoring interface. + label: ROADM degree monitor + icon: mdi:gauge + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnMonitor + - OtnChannelMonitor + order_by: + - name__value + + - name: MuxDemuxMonitor + namespace: Otn + description: The last reading taken from a multiplexer's monitoring interface. + label: Mux/demux monitor + icon: mdi:gauge + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnMonitor + - OtnChannelMonitor + order_by: + - name__value + + - name: RamanMonitor + namespace: Otn + description: The last reading taken from a Raman pump's monitoring interface. + label: Raman monitor + icon: mdi:gauge + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnMonitor + order_by: + - name__value + attributes: + # The same declaration OtnAmplifierMonitor carries. Two kinds, one gain + # reading each, and no gain field on the three kinds that cannot produce + # one. + - name: measured_gain_mdb + kind: Number + parameters: + min_value: 0 + max_value: 40000 + optional: false + description: Gain the amplifier is actually delivering, in millidecibels. + order_weight: 1240 + - name: measured_gain_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if measured_gain_mdb__value is not none %}{{ measured_gain_mdb__value / 1000 }} dB{% endif %} + optional: true + description: Measured gain in dB. Read-only, derived from measured_gain_mdb. + order_weight: 1250 + - name: pump_power_mdbm + kind: Number + parameters: + min_value: 0 + max_value: 40000 + optional: false + description: Raman pump launch power in millidecibel-milliwatts. + order_weight: 1400 + - name: back_reflection_mdb + kind: Number + parameters: + min_value: 0 + max_value: 60000 + optional: false + description: Return loss seen by the pump, in millidecibels. A safety interlock reading. + order_weight: 1410 + + - name: ReceiverMonitor + namespace: Otn + description: The last reading taken from a transponder receiver's monitoring interface. + label: Receiver monitor + icon: mdi:gauge + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnMonitor + order_by: + - name__value + attributes: + # OSNR sits here and on no other monitor kind. A coherent receiver's DSP + # computes it. An amplifier reports power, not OSNR, and after the split + # there is no field on an amplifier monitor to put it in. + - name: measured_osnr_mdb + kind: Number + parameters: + min_value: 0 + max_value: 50000 + optional: false + description: OSNR the receiver reports, in millidecibels. 24.1 dB is 24100. + order_weight: 1500 + - name: measured_osnr_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if measured_osnr_mdb__value is not none %}{{ measured_osnr_mdb__value / 1000 }} dB{% endif %} + optional: true + description: Measured OSNR in dB. Read-only, derived from measured_osnr_mdb. + order_weight: 1510 + - name: rx_power_mdbm + kind: Number + parameters: + min_value: -40000 + max_value: 10000 + optional: false + description: Received power at the coherent front end, in millidecibel-milliwatts. + order_weight: 1520 + # Parts per billion keeps a bit error rate an integer: 2.1e-3 is 2100000. + - name: pre_fec_ber_ppb + kind: Number + parameters: + min_value: 0 + max_value: 1000000000 + optional: false + description: Pre-FEC bit error rate in parts per billion. 2.1e-3 is 2100000. + order_weight: 1530 + - name: q_factor_mdb + kind: Number + parameters: + min_value: 0 + max_value: 30000 + optional: false + description: Q factor in millidecibels. + order_weight: 1540 + - name: cd_fs_per_nm + kind: Number + parameters: + min_value: -100000000 + max_value: 100000000 + optional: false + description: Chromatic dispersion the receiver compensated, in femtoseconds per nanometre. + order_weight: 1550 + - name: dgd_fs + kind: Number + parameters: + min_value: 0 + max_value: 200000 + optional: false + description: Differential group delay in femtoseconds. + order_weight: 1560 + + # The catalog entry and the physical unit. Neither inherits anything: a part + # number is not racked and a pluggable is fitted into a port rather than being + # one. They live beside the ports they plug into, so the file count does not + # move. + - name: TransceiverType + namespace: Otn + description: A pluggable optic part number and what it can be made to do. + label: Transceiver type + icon: mdi:chip + include_in_menu: false + human_friendly_id: + - part_number__value + order_by: + - part_number__value + display_label: part_number__value + attributes: + - name: part_number + kind: Text + unique: true + parameters: + max_length: 64 + regex: "^[A-Za-z0-9._/+-]+$" + optional: false + description: Vendor part number, such as QDD-400G-ZRP-S. + order_weight: 1000 + - name: vendor + kind: Text + parameters: + max_length: 64 + optional: true + order_weight: 1100 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1200 + - name: form_factor + kind: Dropdown + choices: + - name: QSFP-DD + label: QSFP-DD + description: Eight-lane double-density cage. What a coherent 400G pluggable takes. + color: "#2196f3" + - name: QSFP28 + label: QSFP28 + description: Four-lane cage, 100G grey optics. + color: "#4caf50" + - name: OSFP + label: OSFP + description: Eight-lane cage with a larger thermal budget than QSFP-DD. + color: "#9c27b0" + - name: CFP2-DCO + label: CFP2-DCO + description: Digital coherent optic. The DSP is in the module. + color: "#ff9800" + - name: CFP2-ACO + label: CFP2-ACO + description: Analogue coherent optic. The DSP is on the host card. + color: "#ffc107" + - name: SFP28 + label: SFP28 + description: Single-lane 25G cage. + color: "#00bcd4" + - name: SFP+ + label: SFP+ + description: Single-lane 10G cage. + color: "#9e9e9e" + optional: false + description: The cage this part fits. It decides which ports can hold it. + order_weight: 1300 + # A fixed-wavelength optic can only ever sit on the channel it was bought + # for, which is what makes this worth storing rather than deriving. + - name: tunable + kind: Boolean + default_value: false + optional: false + description: Whether the laser can be tuned across the grid or is fixed at one wavelength. + order_weight: 1400 + # Mandatory: a part that runs no mode says nothing about what it can do, and + # transceiver_mode_support has nothing to compare the carrier against. + relationships: + - name: supported_modes + peer: OtnOpticalMode + label: Modes this part can run + kind: Attribute + cardinality: many + optional: false + identifier: otn_optical_mode__transceiver_types + on_delete: no-action + order_weight: 900 + + - name: Transceiver + namespace: Otn + description: One physical pluggable optic, fitted in a port or sitting on a shelf. + label: Transceiver + icon: mdi:memory + include_in_menu: false + human_friendly_id: + - serial__value + order_by: + - serial__value + display_label: "{{ type__part_number__value }} {{ serial__value }}" + attributes: + - name: serial + kind: Text + unique: true + parameters: + max_length: 64 + regex: "^[A-Za-z0-9._-]+$" + optional: false + description: Vendor serial number. The only thing that identifies this unit. + order_weight: 1000 + - name: status + kind: Dropdown + default_value: spare + choices: + - name: in_service + label: In service + color: "#4caf50" + - name: spare + label: Spare + color: "#2196f3" + - name: rma + label: Returned to vendor + color: "#ff9800" + - name: decommissioned + label: Decommissioned + color: "#9e9e9e" + optional: false + order_weight: 1100 + relationships: + - name: type + peer: OtnTransceiverType + label: Part this unit is + kind: Attribute + cardinality: one + optional: false + identifier: otn_transceiver_type__units + on_delete: no-action + order_weight: 900 + # Optional, and therefore carrying no uniqueness constraint: Infrahub + # refuses a constraint on an optional relationship, and making this one + # mandatory would leave a spare, an RMA and a decommissioned unit + # unmodellable. + # + # The duplicate is refused anyway, by the inverse rather than by a + # constraint. OtnLinePort, OtnClientPort and OtnRouterPort each declare a + # cardinality-one `transceiver` on this identifier, so both ends are + # cardinality one and the second module in a port is refused at write time. + # + # The port kind stays with the transceiver_placement check. This peers the + # generic, and a relationship to a generic cannot be filtered by peer kind, + # so the schema accepts a module written into an amplifier port. + - name: port + peer: OtnOpticalPort + label: Port this unit is fitted in + kind: Attribute + cardinality: one + optional: true + identifier: otn_optical_port__transceiver + on_delete: no-action + order_weight: 950 + + # ---- from schemas/otn_plant.yml ---- + - name: FiberType + namespace: Otn + description: Single-mode fiber family. Attenuation, dispersion and group index at 1550 nm. + label: Fiber type + icon: mdi:cable-data + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 32 + regex: "^[A-Za-z0-9.-]+$" + optional: false + description: ITU-T designation, such as G.652.D. + order_weight: 1000 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1100 + # One coefficient per fibre family, measured at 1550 nm, and the budget + # engine applies it to every span it is given. That is right for the dense + # grid, where every channel sits between 1530 and 1565 nm. It is wrong for + # a coarse wavelength, where it understates the loss over the coarse tail + # by about a decibel, more than the margin by which a section in this + # dataset already fails. + # + # So no coarse span reaches the engine: a coarse span belongs to no + # optical multiplex section, and tests/unit/test_geant_dataset.py refuses + # an `oms` on any span touching a site of `site_type: customer`. Budgeting + # a coarse link needs a coefficient per wavelength band, which is a schema + # change. + # + # Named for the coefficient it is, because the plain `attenuation` names + # now belong to the attenuators, which hold an attenuation rather than a + # rate. + - name: attenuation_coefficient_mdb_per_km + kind: Number + parameters: + min_value: 150 + max_value: 400 + optional: false + description: Attenuation at 1550 nm in millidecibels per kilometre. 0.20 dB/km is 200. Wrong for CWDM. + order_weight: 1500 + - name: attenuation_coefficient_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if attenuation_coefficient_mdb_per_km__value is not none %}{{ attenuation_coefficient_mdb_per_km__value + / 1000 }} dB/km{% endif %} + optional: true + description: Attenuation in dB/km. Read-only, derived from attenuation_coefficient_mdb_per_km. + order_weight: 1510 + - name: dispersion_fs_per_nm_km + kind: Number + parameters: + min_value: 0 + max_value: 30000 + optional: false + description: Chromatic dispersion at 1550 nm in femtoseconds per nm per km. 17 ps/nm/km is 17000. + order_weight: 1600 + - name: dispersion_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if dispersion_fs_per_nm_km__value is not none %}{{ dispersion_fs_per_nm_km__value + / 1000 }} ps/nm/km{% endif %} + optional: true + description: Dispersion in ps/nm/km. Read-only, derived from dispersion_fs_per_nm_km. + order_weight: 1610 + - name: group_index_milli + kind: Number + default_value: 1468 + parameters: + min_value: 1400 + max_value: 1500 + optional: false + description: Group index at 1550 nm in milli-units. 1.468 is 1468. Sets propagation delay. + order_weight: 1700 + relationships: + - name: spans + peer: OtnFiberSpan + kind: Attribute + cardinality: many + optional: true + identifier: otn_fiber_type__spans + on_delete: no-action + order_weight: 900 + + - name: Conduit + namespace: Otn + description: Shared-risk link group. Two routes through one conduit are not diverse. + label: Conduit + icon: mdi:pipe + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: "^[A-Za-z0-9._-]+$" + optional: false + order_weight: 1000 + - name: owner + kind: Text + parameters: + max_length: 64 + optional: true + description: Who owns the trench. Often not the network operator. + order_weight: 1100 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1200 + relationships: + - name: spans + peer: OtnFiberSpan + kind: Attribute + cardinality: many + optional: true + identifier: otn_conduit__spans + on_delete: no-action + order_weight: 900 + + - name: FiberSpan + namespace: Otn + description: One amplifier-to-amplifier fiber section. Lossy, but not racked and not a device. + label: Fiber span + icon: mdi:vector-line + include_in_menu: false + inherit_from: + - OtnOpticalElement + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + # element_class is restated to give it a default_value, and the ten choices + # come with it because overriding an inherited Dropdown requires the full + # list. Omitting the `choices` key is rejected at load; a list that is one + # choice short is accepted in silence and only fails when an object writes + # the missing value. tests/unit/test_schema_contract.py holds every + # restatement to the generic's list. + # + # The inherited insertion_loss_mdb stays at its default of 0 on a span. A + # span's real loss is length x attenuation plus splices plus connectors plus + # aging margin, and the budget engine computes it on demand. The stored 0 + # does not mean the span is lossless. + attributes: + - name: element_class + kind: Dropdown + default_value: fiber_span + choices: + - name: transponder + label: Transponder + color: "#2196f3" + - name: roadm + label: ROADM + color: "#9c27b0" + - name: amplifier + label: Amplifier + color: "#ff9800" + - name: mux_demux + label: Mux/demux + color: "#00bcd4" + - name: patch_panel + label: Patch panel + color: "#9e9e9e" + - name: fiber_span + label: Fiber span + color: "#4caf50" + - name: splitter + label: Splitter + color: "#8bc34a" + - name: attenuator + label: Attenuator + color: "#795548" + - name: raman_pump + label: Raman pump + color: "#e91e63" + - name: odu_switch + label: ODU switch + color: "#673ab7" + optional: false + description: What the optical budget engine branches on. + order_weight: 1800 + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: "^[A-Za-z0-9._-]+$" + optional: false + order_weight: 1000 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1100 + # Not unique: a uniqueness constraint cannot reference an optional + # relationship, and oms has to stay optional so a span is creatable before + # its section exists. Duplicate positions are rejected by a check, not by + # the schema. + - name: oms_sequence + kind: Number + parameters: + min_value: 1 + max_value: 50 + optional: true + description: Position of this span within its optical multiplex section, counting from the A end. + order_weight: 1200 + - name: length_m + kind: Number + parameters: + min_value: 0 + max_value: 500000 + optional: false + description: Route length in metres, not straight-line distance. 500 km is the practical ceiling. + order_weight: 1500 + - name: length_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if length_m__value is not none %}{{ length_m__value / 1000 }} km{% endif %} + optional: true + description: Route length in km. Read-only, derived from length_m. + order_weight: 1510 + - name: splice_count + kind: Number + default_value: 0 + parameters: + min_value: 0 + max_value: 500 + optional: false + description: Fusion splices along the span. Roughly one per drum of cable. + order_weight: 1600 + - name: splice_loss_mdb + kind: Number + default_value: 50 + parameters: + min_value: 0 + max_value: 1000 + optional: false + description: Loss per splice in millidecibels. 0.05 dB is 50. + order_weight: 1610 + - name: connector_count + kind: Number + default_value: 2 + parameters: + min_value: 0 + max_value: 20 + optional: false + description: Mated connector pairs on the span, normally one at each end. + order_weight: 1700 + - name: connector_loss_mdb + kind: Number + default_value: 300 + parameters: + min_value: 0 + max_value: 2000 + optional: false + description: Loss per mated connector pair in millidecibels. 0.3 dB is 300. + order_weight: 1710 + - name: aging_margin_mdb + kind: Number + default_value: 1500 + parameters: + min_value: 0 + max_value: 5000 + optional: false + description: Reserved margin for repairs and ageing, in millidecibels. 1.5 dB is 1500. + order_weight: 1720 + relationships: + - name: fiber_type + peer: OtnFiberType + kind: Attribute + cardinality: one + optional: false + identifier: otn_fiber_type__spans + on_delete: no-action + order_weight: 800 + - name: site_a + peer: OtnSite + kind: Attribute + cardinality: one + optional: false + identifier: otn_span__site_a + on_delete: no-action + order_weight: 810 + - name: site_b + peer: OtnSite + kind: Attribute + cardinality: one + optional: false + identifier: otn_span__site_b + on_delete: no-action + order_weight: 820 + - name: conduit + peer: OtnConduit + kind: Attribute + cardinality: one + optional: true + identifier: otn_conduit__spans + on_delete: no-action + order_weight: 830 + - name: oms + peer: OtnOpticalMultiplexSection + kind: Attribute + cardinality: one + optional: true + identifier: otn_oms__spans + on_delete: no-action + order_weight: 840 + # The inverse of OtnRamanPump.span, on the same identifier, and both sides + # are needed. A budget walks a section, reaches its spans and reads what + # is on them; without this side nothing on that walk can see a pump, so + # every pumped span sums to zero gain and the check passes while reporting + # a loss the network does not have. It is also what makes OtnRamanPump + # reachable in the UI, because the sidebar does not name device kinds, and + # tests/unit/test_menu.py asserts that. + - name: raman_pumps + peer: OtnRamanPump + kind: Attribute + cardinality: many + optional: true + identifier: otn_span__raman_pumps + on_delete: no-action + order_weight: 850 + # The ports at each end of the glass. No port kind had any edge to a span or + # a section in either direction, so the only thing naming the degree port + # facing a given span was the far site's shortname inside the port's name + # string, a naming convention doing a relationship's job. + # + # Declared on the span alone: the inverse would put the field on every port + # kind and most ports terminate no span. Empty means unjudgeable rather than + # clean, because an empty list and a fault look the same from outside. + - name: terminating_ports + peer: OtnGenericPort + label: Ports at the ends of this span + kind: Attribute + cardinality: many + optional: true + identifier: otn_span__terminating_ports + on_delete: no-action + order_weight: 950 + + - name: OpticalMultiplexSection + namespace: Otn + description: ROADM to ROADM. Groups the ordered spans and inline amplifiers between two degrees. + label: Optical multiplex section + icon: mdi:ray-start-end + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + # No total_length_m, no total_loss_mdb, no span_count and no hop_count. + # Every reader sums them over the spans instead. + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: "^[A-Za-z0-9._-]+$" + optional: false + order_weight: 1000 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1100 + relationships: + - name: roadm_a + peer: OtnRoadm + kind: Attribute + cardinality: one + optional: false + identifier: otn_oms__roadm_a + on_delete: no-action + order_weight: 800 + - name: roadm_b + peer: OtnRoadm + kind: Attribute + cardinality: one + optional: false + identifier: otn_oms__roadm_b + on_delete: no-action + order_weight: 810 + - name: spans + peer: OtnFiberSpan + kind: Attribute + cardinality: many + optional: true + identifier: otn_oms__spans + on_delete: no-action + order_weight: 900 + # One relationship per direction of travel, rather than one holding both + # chains. Which chain an amplifier is in is then stored by the graph, and + # no attribute on the amplifier can disagree with it. + # + # Both stay optional: a section is creatable before its amplifiers exist. + # A query that selects one and forgets the other is not an error at the + # server, and `peers` hands back an empty list for a key that is not + # there, so the loud failure is the budget engine's N+1 rule instead. + - name: amplifiers_a2b + peer: OtnAmplifier + kind: Attribute + cardinality: many + optional: true + identifier: otn_oms__amplifiers_a2b + on_delete: no-action + order_weight: 910 + - name: amplifiers_b2a + peer: OtnAmplifier + kind: Attribute + cardinality: many + optional: true + identifier: otn_oms__amplifiers_b2a + on_delete: no-action + order_weight: 920 + + # ---- from schemas/otn_logical.yml ---- + - name: FrequencyGrid + namespace: Otn + description: One ITU-T G.694.1 channel. 50 GHz fixed grid, 191.35 to 196.10 THz. + label: Frequency grid channel + icon: mdi:sine-wave + include_in_menu: false + human_friendly_id: + - channel_number__value + order_by: + - channel_number__value + display_label: "Ch{{ channel_number__value }}" + attributes: + - name: channel_number + kind: Number + unique: true + parameters: + min_value: 1 + max_value: 96 + optional: false + description: ITU channel number, 1 to 96. Channel 1 is 191.35 THz. + order_weight: 1000 + # Stored rather than computed so that "which channel is 193.5 THz" is a + # GraphQL range filter instead of a Python loop. A guard test recomputes + # all 96 from units.py. + - name: center_frequency_mhz + kind: Number + unique: true + parameters: + min_value: 191350000 + max_value: 196100000 + optional: false + description: Centre frequency in MHz. Channel n is 191350000 + (n - 1) x 50000. + order_weight: 1500 + - name: center_frequency_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if center_frequency_mhz__value is not none %}{{ center_frequency_mhz__value / 1000000 }} THz{% endif %} + optional: true + description: Centre frequency in THz. Byte-identical to the optical-port rendering. + order_weight: 1510 + # The inverse of OtnOpticalCarrier.channel, on the same identifier, so a + # channel's page answers "who holds this wavelength". + relationships: + - name: carriers + peer: OtnOpticalCarrier + label: Carriers on this channel + kind: Attribute + cardinality: many + optional: true + identifier: otn_carrier__channel + on_delete: no-action + order_weight: 900 + + # The coarse plan, G.694.2, in its own kind. `OtnOpticalCarrier.channel` is + # mandatory, cardinality one, and peers `OtnFrequencyGrid`, so the server + # rejects a carrier pointed at a coarse wavelength with "must be of type: + # ['OtnFrequencyGrid']". Holding both plans in one kind would turn that + # write-time rejection into a check that runs in a pipeline. + - name: CwdmChannel + namespace: Otn + description: One ITU-T G.694.2 coarse wavelength. 20 nm spacing, 1271 to 1611 nm. + label: CWDM wavelength + icon: mdi:palette-swatch-variant + include_in_menu: false + human_friendly_id: + - center_wavelength_nm__value + order_by: + - center_wavelength_nm__value + display_label: "{{ center_wavelength_nm__value }} nm" + attributes: + - name: center_wavelength_nm + kind: Number + unique: true + parameters: + min_value: 1271 + max_value: 1611 + optional: false + description: Nominal central wavelength in nm. Wavelength n is 1271 + (n - 1) x 20. + order_weight: 1000 + # Stored rather than computed, for the reason center_frequency_mhz is + # stored above: "which coarse wavelengths could an erbium amplifier reach" + # is then a GraphQL filter instead of a Python loop. A guard test + # recomputes all eighteen wavelengths and all eighteen bands from units.py. + - name: band + kind: Dropdown + choices: + - name: o + label: O band + color: "#607d8b" + - name: e + label: E band + color: "#795548" + - name: s + label: S band + color: "#009688" + - name: c + label: C band + color: "#2196f3" + - name: l + label: L band + color: "#9c27b0" + optional: false + description: ITU band. Only the two C-band wavelengths sit in the erbium window. + order_weight: 1100 + + - name: OpticalMode + namespace: Otn + description: What a transponder or coherent pluggable can do. Reach and required OSNR are data, not assumptions. + label: Optical mode + icon: mdi:waveform + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + # The regex admits a space, a plus and a slash, which the device-name + # regex does not: "OpenZR+ 400G" and "DP-16QAM 64GBd 400G" both need it. + # A human_friendly_id tolerates both characters. + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: "^[A-Za-z0-9 .+/_-]+$" + optional: false + order_weight: 1000 + - name: mode_class + kind: Dropdown + default_value: transponder + choices: + - name: transponder + label: Transponder + color: "#2196f3" + - name: pluggable + label: Coherent pluggable + color: "#4caf50" + optional: false + description: Two values, and the pluggable-reach report filters on it. + order_weight: 1100 + - name: modulation + kind: Text + enum: + - DP-QPSK + - DP-8QAM + - DP-16QAM + - DP-64QAM + optional: false + order_weight: 1200 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1300 + - name: line_rate_gbps + kind: Number + parameters: + min_value: 1 + max_value: 1600 + optional: false + description: Client-side capacity in Gbps. Already a whole number in its natural unit. + order_weight: 1500 + - name: baud_mbaud + kind: Number + parameters: + min_value: 1000 + max_value: 200000 + optional: false + description: Symbol rate in megabaud. 59.84 GBd is 59840. Sets the spectral width. + order_weight: 1600 + - name: required_osnr_mdb + kind: Number + parameters: + min_value: 5000 + max_value: 40000 + optional: false + description: OSNR needed at the receiver, in millidecibels. 26 dB is 26000. + order_weight: 1700 + - name: required_osnr_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if required_osnr_mdb__value is not none %}{{ required_osnr_mdb__value / 1000 }} dB{% endif %} + optional: true + description: Required OSNR in dB. Read-only, derived from required_osnr_mdb. + order_weight: 1710 + - name: cd_tolerance_fs_per_nm + kind: Number + parameters: + min_value: 0 + max_value: 200000000 + optional: false + description: Chromatic dispersion the receiver compensates, in fs/nm. 100000 ps/nm is 100000000. + order_weight: 1800 + - name: nominal_reach_m + kind: Number + parameters: + min_value: 0 + max_value: 5000000 + optional: false + description: Vendor-quoted reach in metres. A starting point for the budget, not a guarantee. + order_weight: 1900 + - name: nominal_reach_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if nominal_reach_m__value is not none %}{{ nominal_reach_m__value / 1000 }} km{% endif %} + optional: true + description: Nominal reach in km. Read-only, derived from nominal_reach_m. + order_weight: 1910 + - name: fec_type + kind: Text + default_value: SD-FEC + enum: + - none + - GFEC + - SD-FEC + - cFEC + - oFEC + optional: false + order_weight: 1950 + - name: fec_latency_ns + kind: Number + default_value: 0 + parameters: + min_value: 0 + max_value: 100000 + optional: false + description: Encoder and decoder latency in nanoseconds. Small next to propagation, but not zero. + order_weight: 1960 + # The inverse of OtnOpticalCarrier.optical_mode, on the same identifier, so + # a mode's page answers which carriers use it. + relationships: + - name: carriers + peer: OtnOpticalCarrier + label: Carriers using this mode + kind: Attribute + cardinality: many + optional: true + identifier: otn_carrier__optical_mode + on_delete: no-action + order_weight: 900 + + - name: ClientSignal + namespace: Otn + description: What a customer hands over, and the container it maps into. + label: Client signal + icon: mdi:import + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 32 + regex: "^[A-Za-z0-9./_-]+$" + optional: false + order_weight: 1000 + - name: alias + kind: Text + parameters: + max_length: 32 + optional: true + description: The SONET name where one exists. STM-16 is OC-48. + order_weight: 1100 + - name: layer + kind: Dropdown + choices: + - name: ethernet + label: Ethernet + color: "#2196f3" + - name: sdh + label: SDH / SONET + color: "#9c27b0" + - name: pdh + label: PDH + color: "#795548" + - name: fibre_channel + label: Fibre Channel + color: "#ff9800" + # Being declared here says nothing about automatic selection. That + # is `auto_selectable` below, and it is per signal, not per layer. + - name: infiniband + label: InfiniBand + color: "#673ab7" + optional: false + description: Grouping the catalog by layer is the first thing a reader does. + order_weight: 1200 + # Whether the rate rule may pick this signal when a service states none. + # + # Per signal, where the Python allow-list it replaces was per layer. + # Finer, and strictly more expressive: two rows on one layer may differ. + # The measured reason every InfiniBand row is false is recorded in + # `objects/04_client_signals.yml`. + - name: auto_selectable + kind: Boolean + # `default_value: false` with `optional: false` fails closed: a row added + # without a decision is unreachable by the automatic path until somebody + # writes `true` in a diff. A default of true would fail open, and the + # next specialised signal would be handed to a service that never asked + # for it, silently. That is the defect this flag exists to prevent. + default_value: false + optional: false + description: May the rate rule pick this signal when a service states none. + order_weight: 1250 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1300 + # kbps, not Mbps, for the same reason OtnCopperPort.speed_kbps is: E1 is + # 2.048 Mbps and rounds to 2 as an integer. + - name: bit_rate_kbps + kind: Number + parameters: + min_value: 64 + max_value: 1600000000 + optional: false + description: Nominal line rate in kbps. E1 is 2048, 100GBASE-LR4 is 103100000. + order_weight: 1500 + # Two divisors in one template. The catalog spans seven orders of + # magnitude, so a single divisor renders E1 as 0.002048 Gbps or + # 400GBASE-FR4 as 412500.0 Mbps. + - name: bit_rate_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if bit_rate_kbps__value is none %}{% elif bit_rate_kbps__value + >= 1000000 %}{{ bit_rate_kbps__value / 1000000 }} Gbps{% else %}{{ + bit_rate_kbps__value / 1000 }} Mbps{% endif %} + optional: true + description: Bit rate in Mbps below one gigabit, Gbps at or above it. + order_weight: 1510 + - name: default_container_type + kind: Text + enum: + - ODU0 + - ODU1 + - ODU2 + - ODU2e + - ODU3 + - ODU4 + - ODUC1 + - ODUC2 + - ODUC3 + - ODUC4 + - ODUflex + - VC-12 + - VC-4 + - STM-N + optional: false + description: The first step of the mapping chain, not the last. E1 maps into VC-12, not ODU1. + order_weight: 1600 + - name: default_mapping + kind: Text + enum: + - GMP + - BMP + - AMP + optional: false + description: ITU-T G.709 mapping procedure. Generic, bit-synchronous or asynchronous. + order_weight: 1700 + relationships: + - name: containers + peer: OtnContainer + kind: Attribute + cardinality: many + optional: true + identifier: otn_client_signal__containers + on_delete: no-action + order_weight: 900 + # The inverse of OtnService.client_signal, so a catalog row's page answers + # "which services hand this over". + - name: services + peer: OtnService + kind: Attribute + cardinality: many + optional: true + identifier: otn_service__client_signal + on_delete: no-action + order_weight: 910 + + - name: Container + namespace: Otn + description: ODU or SDH virtual container. The digital adaptation layer between a client and a wavelength. + label: Container + icon: mdi:package-variant-closed + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: "^[A-Za-z0-9._-]+$" + optional: false + order_weight: 1000 + # Sixteen values: ClientSignal.default_container_type's fourteen, plus + # ODUC6 and ODUC8. Those two are line containers for the 600G and 800G + # modes and no client maps directly into one, so they belong here and not + # in the client enum. + # VC-12, VC-4 and STM-N are here because an E1 maps into VC-12 into STM-N + # into ODU1, and without them that chain cannot be written at all. + - name: odu_type + kind: Text + enum: + - ODU0 + - ODU1 + - ODU2 + - ODU2e + - ODU3 + - ODU4 + - ODUC1 + - ODUC2 + - ODUC3 + - ODUC4 + - ODUC6 + - ODUC8 + - ODUflex + - VC-12 + - VC-4 + - STM-N + optional: false + order_weight: 1100 + - name: mapping_mode + kind: Text + default_value: GMP + enum: + - GMP + - BMP + - AMP + optional: false + order_weight: 1200 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1300 + # Which segment of its circuit this container rides. The same field and + # the same default as OtnOpticalPath.segment_sequence, and read the same + # way: a container written before this feature rode the one and only + # segment, so 1 is the correct value and not just a value that loads. + # + # A circuit is therefore its containers ordered by this, and its + # wavelengths are its paths ordered by this. Two readings of one sequence + # from two places, and a test asserts they agree. + - name: segment_sequence + kind: Number + default_value: 1 + parameters: + min_value: 1 + max_value: 50 + optional: false + description: Which segment of its circuit this container rides. 1 for a circuit that spans one wavelength. + order_weight: 1400 + # Two numbers, not one. tributary_slots is what this container occupies in + # its parent; tributary_slot_capacity is what it offers to its children. + # The capacity check sums the first across children and compares it to + # the second on the parent, which needs both to exist. + # + # 640 on both bounds is ODUC8, the widest row in containers.SLOT_TABLE and + # the line container for the fastest mode in the catalog. A bound below the + # widest row rejects a container the slot arithmetic considers legal, so + # `tests/unit/test_containers.py` recomputes 640 from the table and fails + # if either number moves alone. + - name: tributary_slots + kind: Number + default_value: 0 + parameters: + min_value: 0 + max_value: 640 + optional: false + description: Slots this container occupies in its parent. An ODU2 in an ODU4 takes 8. + order_weight: 1500 + - name: tributary_slot_capacity + kind: Number + default_value: 0 + parameters: + min_value: 0 + max_value: 640 + optional: false + description: Slots this container offers to its children. An ODU4 offers 80. + order_weight: 1600 + # The two `direction` keys are mandatory. Both sides default to + # bidirectional, which collides on a shared identifier and is rejected at + # load time. outbound on the parent side and inbound on the child side + # resolves to one edge: write only the child's parent_container and the + # parent reports the child with no second write. + relationships: + # The wavelength this container rides. Optional, because a container may + # be multiplexed into a parent that holds the carrier instead. + - name: carrier + peer: OtnOpticalCarrier + kind: Attribute + cardinality: one + optional: true + identifier: otn_carrier__containers + on_delete: no-action + order_weight: 790 + - name: client_signal + peer: OtnClientSignal + kind: Attribute + cardinality: one + optional: true + identifier: otn_client_signal__containers + on_delete: no-action + order_weight: 800 + - name: parent_container + peer: OtnContainer + kind: Attribute + cardinality: one + optional: true + identifier: otn_container__children + direction: outbound + on_delete: no-action + order_weight: 810 + # Which circuit owns this container. Only a client container carries one. A + # line container leaves it empty on purpose: it belongs to a wavelength, + # and naming a service on it would claim that one of the several services + # groomed into that wavelength owns the whole of it. + - name: service + # Stored, not derived, and it cannot be derived. The chain container to + # parent_container to carrier to optical_path to service does exist, but + # `OtnOpticalCarrier.optical_path` is cardinality many since grooming + # let two services share a wavelength, so that walk yields every service + # on the wavelength rather than the one that owns this container. That + # is the right answer for `transforms/impact_report.py`, whose question + # is who is affected by a cut, and the wrong one for + # `transforms/service_trace.py`, which listed a neighbour's containers + # as part of a service's own circuit with nothing raised. The naming + # convention `odu-` was the only link before this, and nothing + # read it. + peer: OtnService + kind: Attribute + cardinality: one + optional: true + identifier: otn_service__containers + on_delete: no-action + order_weight: 820 + - name: child_containers + peer: OtnContainer + kind: Attribute + cardinality: many + optional: true + identifier: otn_container__children + direction: inbound + on_delete: no-action + order_weight: 900 + + # ---- from schemas/otn_devices.yml ---- + - name: Router + namespace: Otn + description: IP router. Light terminates here, so it contributes no insertion loss. + label: Router + icon: mdi:router + include_in_menu: false + inherit_from: + - OtnGenericDevice + + - name: Transponder + namespace: Otn + description: Client to DWDM line adaptation. + label: Transponder + icon: mdi:transit-connection-variant + include_in_menu: false + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + attributes: + - name: element_class + kind: Dropdown + default_value: transponder + choices: + - name: transponder + label: Transponder + color: "#2196f3" + - name: roadm + label: ROADM + color: "#9c27b0" + - name: amplifier + label: Amplifier + color: "#ff9800" + - name: mux_demux + label: Mux/demux + color: "#00bcd4" + - name: patch_panel + label: Patch panel + color: "#9e9e9e" + - name: fiber_span + label: Fiber span + color: "#4caf50" + - name: splitter + label: Splitter + color: "#8bc34a" + - name: attenuator + label: Attenuator + color: "#795548" + - name: raman_pump + label: Raman pump + color: "#e91e63" + - name: odu_switch + label: ODU switch + color: "#673ab7" + optional: false + description: What the optical budget engine branches on. + order_weight: 1800 + + - name: Roadm + namespace: Otn + description: Reconfigurable optical add/drop multiplexer. + label: ROADM + icon: mdi:call-split + include_in_menu: false + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + attributes: + - name: element_class + kind: Dropdown + default_value: roadm + choices: + - name: transponder + label: Transponder + color: "#2196f3" + - name: roadm + label: ROADM + color: "#9c27b0" + - name: amplifier + label: Amplifier + color: "#ff9800" + - name: mux_demux + label: Mux/demux + color: "#00bcd4" + - name: patch_panel + label: Patch panel + color: "#9e9e9e" + - name: fiber_span + label: Fiber span + color: "#4caf50" + - name: splitter + label: Splitter + color: "#8bc34a" + - name: attenuator + label: Attenuator + color: "#795548" + - name: raman_pump + label: Raman pump + color: "#e91e63" + - name: odu_switch + label: ODU switch + color: "#673ab7" + optional: false + description: What the optical budget engine branches on. + order_weight: 1800 + # The inverses of OtnOpticalMultiplexSection.roadm_a and .roadm_b, each on + # the identifier its forward side declares. A ROADM inherits site at 800 and + # ports at 900, so these follow at 960 and 970. + relationships: + - name: sections_a + peer: OtnOpticalMultiplexSection + label: Sections (A end) + kind: Attribute + cardinality: many + optional: true + identifier: otn_oms__roadm_a + on_delete: no-action + order_weight: 960 + - name: sections_b + peer: OtnOpticalMultiplexSection + label: Sections (B end) + kind: Attribute + cardinality: many + optional: true + identifier: otn_oms__roadm_b + on_delete: no-action + order_weight: 970 + + - name: Amplifier + namespace: Otn + description: Inline, booster or pre-amplifier. + label: Amplifier + icon: mdi:amplifier + include_in_menu: false + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + # oms_sequence is stored because Infrahub relationships carry no order, so + # OtnOpticalMultiplexSection.amplifiers hands back a set. The budget cannot + # be walked over a set: the loss ahead of an amplifier is the loss of + # whatever sits immediately before it. OtnFiberSpan carries an explicit + # position for the same reason. + attributes: + - name: element_class + kind: Dropdown + default_value: amplifier + choices: + - name: transponder + label: Transponder + color: "#2196f3" + - name: roadm + label: ROADM + color: "#9c27b0" + - name: amplifier + label: Amplifier + color: "#ff9800" + - name: mux_demux + label: Mux/demux + color: "#00bcd4" + - name: patch_panel + label: Patch panel + color: "#9e9e9e" + - name: fiber_span + label: Fiber span + color: "#4caf50" + - name: splitter + label: Splitter + color: "#8bc34a" + - name: attenuator + label: Attenuator + color: "#795548" + - name: raman_pump + label: Raman pump + color: "#e91e63" + - name: odu_switch + label: ODU switch + color: "#673ab7" + optional: false + description: What the optical budget engine branches on. + order_weight: 1800 + # 3000 is the quantum-limited floor for an EDFA and 10000 is a bad one, so + # the bounds bracket the whole plausible range rather than this dataset's. + - name: noise_figure_mdb + kind: Number + default_value: 4000 + parameters: + min_value: 3000 + max_value: 10000 + optional: false + description: Noise figure in millidecibels. 4.0 dB is 4000. Sets the ASE this stage adds. + order_weight: 1520 + - name: noise_figure_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if noise_figure_mdb__value is not none %}{{ noise_figure_mdb__value / 1000 }} dB{% endif %} + optional: true + description: Noise figure in dB. Read-only, derived from noise_figure_mdb. + order_weight: 1530 + - name: gain_mdb + kind: Number + default_value: 22000 + parameters: + min_value: 0 + max_value: 40000 + optional: false + description: Gain in millidecibels. 22.0 dB is 22000. Must cover the loss ahead of the input. + order_weight: 1540 + - name: gain_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if gain_mdb__value is not none %}{{ gain_mdb__value / 1000 }} dB{% endif %} + optional: true + description: Gain in dB. Read-only, derived from gain_mdb. + order_weight: 1550 + # No direction attribute. An amplifier hut is bidirectional and this model + # gives each direction its own object, but which chain an object is in is + # the relationship holding it, oms_a2b or oms_b2a, and an attribute + # restating that would be a second copy that can disagree with the first. + # + # It does two jobs and both are its own. It orders a chain that the + # relationship has already identified, so a sorted chain is in traversal + # order and the engine never reverses one. And it fixes which member of + # that chain is which: position 1 is the booster, position N+1 is the + # pre-amplifier, and amplifier k feeds span k of its own walk. + - name: oms_sequence + kind: Number + parameters: + min_value: 1 + # 51, not 50: a section with N spans carries N+1 amplifiers per + # direction, and the span attribute caps N at 50. + max_value: 51 + # Mandatory, and with no default_value on purpose. A default of 1 would + # let a new amplifier take a silent position at the head of its chain, + # collide with the real first amplifier, and sort stably into a wrong + # answer rather than raising. + # + # Not unique, and it cannot be: a uniqueness constraint cannot reference + # an optional relationship, and the section relationships have to stay + # optional so an amplifier is creatable before its section exists. + # Rejecting duplicate positions is a test's job. + optional: false + description: Position in this amplifier's own chain, counting along the direction it amplifies. + order_weight: 1560 + # The two inverses of the section's two amplifier relationships, each on the + # matching identifier. Exactly one is set on any amplifier, and which one it + # is answers the direction question the deleted attribute used to answer. + # + # Two, not one, because one relationship cannot be the inverse of two + # identifiers. The alternative was dropping the amplifier's section + # relationship entirely and reaching the section by server-side filter, and + # that was rejected: an amplifier page is a page an operator lands on, and + # "which section and which way" is what it is for. The honest accounting is + # that this trades one attribute for one relationship and the schema does + # not get shorter. + relationships: + - name: oms_a2b + peer: OtnOpticalMultiplexSection + kind: Attribute + cardinality: one + # Both optional, because an amplifier is creatable before its section + # exists. + optional: true + identifier: otn_oms__amplifiers_a2b + on_delete: no-action + order_weight: 950 + - name: oms_b2a + peer: OtnOpticalMultiplexSection + kind: Attribute + cardinality: one + optional: true + identifier: otn_oms__amplifiers_b2a + on_delete: no-action + order_weight: 960 + + - name: MuxDemux + namespace: Otn + description: Passive multiplexer. A dense AWG on the core, a coarse thin-film filter on a tail. + label: Mux/demux + icon: mdi:call-merge + include_in_menu: false + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + attributes: + - name: element_class + kind: Dropdown + default_value: mux_demux + choices: + - name: transponder + label: Transponder + color: "#2196f3" + - name: roadm + label: ROADM + color: "#9c27b0" + - name: amplifier + label: Amplifier + color: "#ff9800" + - name: mux_demux + label: Mux/demux + color: "#00bcd4" + - name: patch_panel + label: Patch panel + color: "#9e9e9e" + - name: fiber_span + label: Fiber span + color: "#4caf50" + - name: splitter + label: Splitter + color: "#8bc34a" + - name: attenuator + label: Attenuator + color: "#795548" + - name: raman_pump + label: Raman pump + color: "#e91e63" + - name: odu_switch + label: ODU switch + color: "#673ab7" + optional: false + description: What the optical budget engine branches on. + order_weight: 1800 + relationships: + # The wavelengths a coarse multiplexer lights are a property of the device + # rather than of the plan. Without this relationship OtnCwdmChannel is a + # kind nothing points at, which fails the sidebar reachability test in + # tests/unit/test_menu.py. + - name: cwdm_channels + peer: OtnCwdmChannel + kind: Attribute + cardinality: many + optional: true + identifier: otn_mux_demux__cwdm_channels + on_delete: no-action + order_weight: 1900 + + - name: PatchPanel + namespace: Otn + description: Optical distribution frame. Carries connector loss. + label: Patch panel + icon: mdi:view-grid-outline + include_in_menu: false + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + attributes: + - name: element_class + kind: Dropdown + default_value: patch_panel + choices: + - name: transponder + label: Transponder + color: "#2196f3" + - name: roadm + label: ROADM + color: "#9c27b0" + - name: amplifier + label: Amplifier + color: "#ff9800" + - name: mux_demux + label: Mux/demux + color: "#00bcd4" + - name: patch_panel + label: Patch panel + color: "#9e9e9e" + - name: fiber_span + label: Fiber span + color: "#4caf50" + - name: splitter + label: Splitter + color: "#8bc34a" + - name: attenuator + label: Attenuator + color: "#795548" + - name: raman_pump + label: Raman pump + color: "#e91e63" + - name: odu_switch + label: ODU switch + color: "#673ab7" + optional: false + description: What the optical budget engine branches on. + order_weight: 1800 + + - name: RamanPump + namespace: Otn + description: Pump laser injecting Raman gain into one fiber span. + label: Raman pump + icon: mdi:laser-pointer + include_in_menu: false + # Two generics, listed side by side. Infrahub's GenericSchemaWrite has no + # inherit_from key, so OtnOpticalElement cannot inherit OtnGenericDevice and + # a kind that is both a racked device and something light passes through has + # to say so twice. + # + # No attribute storing which direction the pump amplifies. That is the + # conclusion, and injection_end and propagation are the two physical facts + # that compute it: a counter-propagating pump fires back up the fibre from + # the far end, so one at the B end amplifies the A to B signal, and a + # co-propagating pump at the A end amplifies it too. + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + attributes: + - name: element_class + kind: Dropdown + default_value: raman_pump + choices: + - name: transponder + label: Transponder + color: "#2196f3" + - name: roadm + label: ROADM + color: "#9c27b0" + - name: amplifier + label: Amplifier + color: "#ff9800" + - name: mux_demux + label: Mux/demux + color: "#00bcd4" + - name: patch_panel + label: Patch panel + color: "#9e9e9e" + - name: fiber_span + label: Fiber span + color: "#4caf50" + - name: splitter + label: Splitter + color: "#8bc34a" + - name: attenuator + label: Attenuator + color: "#795548" + - name: raman_pump + label: Raman pump + color: "#e91e63" + - name: odu_switch + label: ODU switch + color: "#673ab7" + optional: false + description: What the optical budget engine branches on. + order_weight: 1800 + # On-off gain: the difference in received signal power with the pump on + # against the pump off. It reduces the effective loss of the span it sits + # on rather than adding a stage to the chain, which is why the budget + # subtracts it from the fiber loss and no amplifier object is involved. + - name: on_off_gain_mdb + kind: Number + default_value: 10000 + parameters: + min_value: 0 + max_value: 15000 + optional: false + description: Raman on-off gain in millidecibels. 10.0 dB is 10000. + order_weight: 1520 + - name: on_off_gain_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if on_off_gain_mdb__value is not none %}{{ on_off_gain_mdb__value / 1000 }} dB{% endif %} + optional: true + description: On-off gain in dB. Read-only, derived from on_off_gain_mdb. + order_weight: 1530 + # Where the pump is spliced in. The two choices name the span's own + # endpoint relationships, site_a and site_b, rather than inventing a + # vocabulary. + # + # default_value exists because a mandatory attribute needs one for the + # schema to load, and site_b is what every shipped counter-propagating + # forward pump takes. It is a loading device and not an assertion: the + # generator writes the value explicitly on every pump, and injection_end + # is in NEVER_SUPPRESSED so the generated file always shows it. + - name: injection_end + kind: Dropdown + default_value: site_b + choices: + - name: site_a + label: Site A end + color: "#2196f3" + - name: site_b + label: Site B end + color: "#ff5722" + optional: false + description: Which end of its span the pump is injected at, named against the span's own site_a and site_b. + order_weight: 1540 + # Counter-propagating is the default: the pump fires against the signal, + # so pump noise is averaged over the span instead of landing on the signal + # at its weakest point. The budget reads this and injection_end together: + # the direction a pump amplifies is + # (injection_end == site_a) == (propagation == co). + - name: propagation + kind: Dropdown + default_value: counter + choices: + - name: counter + label: Counter-propagating + color: "#3f51b5" + - name: co + label: Co-propagating + color: "#009688" + optional: false + description: Whether the pump fires against the signal or with it. + order_weight: 1550 + # Mandatory: a detached pump is gain the budget can never find. The inverse + # `raman_pumps` lives on OtnFiberSpan in otn_plant.yml and carries the same + # identifier. + relationships: + - name: span + peer: OtnFiberSpan + kind: Attribute + cardinality: one + optional: false + identifier: otn_span__raman_pumps + on_delete: no-action + order_weight: 850 + + - name: OduSwitch + namespace: Otn + description: O-E-O device. Terminates one wavelength and originates the next, as a + regenerator or an ODU cross-connect. + label: ODU switch + icon: mdi:swap-horizontal-variant + include_in_menu: false + # Two flat generics side by side, the composition OtnMuxDemux already uses. + # OtnOpticalElement is here because OtnPathHop.element peers that generic, + # and a segment's route has to be able to name the device it terminates on. + # + # insertion_loss_mdb arrives with OtnOpticalElement and applies to the + # incoming segment only. This device terminates the light rather than + # passing it through, so the outgoing segment starts at a transmitter and + # not at an attenuated signal. That asymmetry is why the loss cannot be + # added into one total spanning both segments: each segment carries its own + # budget and the device is the boundary between them. + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + # element_class defaults to `odu_switch`, the tenth choice, added when the + # first object forced the decision. None of the original nine names an O-E-O + # device, so the alternative was defaulting to `transponder` or `roadm`, + # which puts a false value on the one attribute that says what a device is. + # Nothing in the repository reads element_class, so a wrong label costs + # nothing mechanically and is simply untrue in the data, which is the whole + # argument for paying for the tenth choice instead. + attributes: + # The list now lives in eleven blocks, enumerated in otn_base.yml. A block + # missed is a dropdown offering different options depending on the kind you + # look at, and the server accepts that in silence. + - name: element_class + kind: Dropdown + default_value: odu_switch + choices: + - name: transponder + label: Transponder + color: "#2196f3" + - name: roadm + label: ROADM + color: "#9c27b0" + - name: amplifier + label: Amplifier + color: "#ff9800" + - name: mux_demux + label: Mux/demux + color: "#00bcd4" + - name: patch_panel + label: Patch panel + color: "#9e9e9e" + - name: fiber_span + label: Fiber span + color: "#4caf50" + - name: splitter + label: Splitter + color: "#8bc34a" + - name: attenuator + label: Attenuator + color: "#795548" + - name: raman_pump + label: Raman pump + color: "#e91e63" + - name: odu_switch + label: ODU switch + color: "#673ab7" + optional: false + description: What the optical budget engine branches on. + order_weight: 1800 + # `regenerator` is the default because it is the cheaper device and the + # one a route that does not close needs: it carries the whole payload + # across without looking inside it. `cross_connect` demultiplexes to + # containers and regroups them, which is what lets the segments either + # side carry different clients. + - name: switching_mode + kind: Dropdown + default_value: regenerator + choices: + - name: regenerator + label: Regenerator + color: "#3f51b5" + - name: cross_connect + label: ODU cross-connect + color: "#009688" + optional: false + description: Whether the device carries the whole payload through or demultiplexes and regroups containers. + order_weight: 1300 + # Nanoseconds as an integer, the unit OtnOpticalMode.fec_latency_ns and + # OtnOpticalPath.latency_ns already use. No Float kind exists. + # + # No paired _display, for the reason fec_latency_ns has none: the whole + # plausible range is hundreds of nanoseconds, and the repository's only + # nanosecond rendering divides by 1000, so every device would read + # "0.0 us". The figure an operator wants is the circuit total, and + # latency_display on the path renders that. + - name: framing_latency_ns + kind: Number + # A device nobody has characterised adds nothing to the circuit rather + # than adding a guess. + default_value: 0 + parameters: + min_value: 0 + # fec_latency_ns's ceiling, reused. A framing delay of 100 us would be + # the largest single term in the whole latency budget. + max_value: 100000 + optional: false + description: Delay this device adds in nanoseconds, from framing and from the electrical crossing. + order_weight: 1520 + # The wavelengths this device terminates. It is what + # src/infrahub_demo_otn/chains.py reads to learn which carriers a junction + # can join, and it is the whole of the junction predicate: a device with an + # empty list contributes no junction. + relationships: + # It is not what finds the chain, and R-008 measured that rather than + # assuming it. A device-to-device traversal filtered on this edge alone + # returns zero paths, because a ROADM has no edge to a carrier and a + # carrier has no edge to a device other than this one. Widen the filter + # until paths come back and dropping this edge from it changes nothing + # about which paths come back: 100 either way, and 48 of those join two + # carriers at the one section all 71 of them cross, with nothing there to + # terminate the light and re-originate it. Choosing the cover is a search + # over the carriers this relationship names, not a traversal this + # relationship makes possible. + # + # Attachment, not termination: `oxc-mil-01` is patched to 37 wavelengths + # that terminate on Milan transponders. `OtnLinePort.carrier` is the + # termination answer. The identifier stays put; Infrahub freezes it at + # first load and rejects a change with `not_supported`. + - name: carriers + peer: OtnOpticalCarrier + label: Carriers patched to this shelf + kind: Attribute + cardinality: many + optional: true + identifier: otn_odu_switch__carriers + # Matching the other side and every other cross-reference here. A device + # and a wavelength are independent objects: deleting either one leaves + # the other. + on_delete: no-action + order_weight: 1900 + + # Two attenuator kinds rather than one carrying an attenuator_type Dropdown and + # an optional range. That is the split the five monitor kinds already argue + # for: a pad with a maximum is a field that should not exist, and with two + # kinds it does not. + # + # Neither kind declares ports. Both inherit `ports` from OtnGenericDevice and + # hold none, and both reach a path through OtnPathHop.element the way + # OtnFiberSpan does. + # + # The budget engine adds attenuation_mdb to the inherited insertion_loss_mdb + # itself. No summed total is stored: one number could not say which half is the + # device and which is the setting. + - name: FixedAttenuator + namespace: Otn + description: A pad. One fixed amount of loss, patched into a link that arrives too hot. + label: Fixed attenuator + icon: mdi:filter-outline + include_in_menu: false + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + attributes: + - name: element_class + kind: Dropdown + default_value: attenuator + choices: + - name: transponder + label: Transponder + color: "#2196f3" + - name: roadm + label: ROADM + color: "#9c27b0" + - name: amplifier + label: Amplifier + color: "#ff9800" + - name: mux_demux + label: Mux/demux + color: "#00bcd4" + - name: patch_panel + label: Patch panel + color: "#9e9e9e" + - name: fiber_span + label: Fiber span + color: "#4caf50" + - name: splitter + label: Splitter + color: "#8bc34a" + - name: attenuator + label: Attenuator + color: "#795548" + - name: raman_pump + label: Raman pump + color: "#e91e63" + - name: odu_switch + label: ODU switch + color: "#673ab7" + optional: false + description: What the optical budget engine branches on. + order_weight: 1800 + - name: attenuation_mdb + kind: Number + default_value: 0 + parameters: + min_value: 0 + max_value: 30000 + optional: false + description: Attenuation the pad is set to, in millidecibels. 5.0 dB is 5000. + order_weight: 1900 + - name: attenuation_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if attenuation_mdb__value is not none %}{{ attenuation_mdb__value / 1000 }} dB{% endif %} + optional: true + description: Attenuation in dB. Read-only, derived from attenuation_mdb. + order_weight: 1910 + + - name: VariableAttenuator + namespace: Otn + description: A VOA. The same loss, dialled rather than fixed, within the range the hardware has. + label: Variable attenuator + icon: mdi:tune-variant + include_in_menu: false + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + attributes: + - name: element_class + kind: Dropdown + default_value: attenuator + choices: + - name: transponder + label: Transponder + color: "#2196f3" + - name: roadm + label: ROADM + color: "#9c27b0" + - name: amplifier + label: Amplifier + color: "#ff9800" + - name: mux_demux + label: Mux/demux + color: "#00bcd4" + - name: patch_panel + label: Patch panel + color: "#9e9e9e" + - name: fiber_span + label: Fiber span + color: "#4caf50" + - name: splitter + label: Splitter + color: "#8bc34a" + - name: attenuator + label: Attenuator + color: "#795548" + - name: raman_pump + label: Raman pump + color: "#e91e63" + - name: odu_switch + label: ODU switch + color: "#673ab7" + optional: false + description: What the optical budget engine branches on. + order_weight: 1800 + - name: attenuation_mdb + kind: Number + default_value: 0 + parameters: + min_value: 0 + max_value: 30000 + optional: false + description: Attenuation the pad is set to, in millidecibels. 5.0 dB is 5000. + order_weight: 1900 + - name: attenuation_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if attenuation_mdb__value is not none %}{{ attenuation_mdb__value / 1000 }} dB{% endif %} + optional: true + description: Attenuation in dB. Read-only, derived from attenuation_mdb. + order_weight: 1910 + # The absolute ceiling is on the attribute, so the schema refuses a + # physically impossible figure at write time. It cannot refuse a setting + # past this device's own maximum, a sibling attribute's value, so + # attenuator_range owns that half and only that half. + - name: max_attenuation_mdb + kind: Number + parameters: + min_value: 0 + max_value: 30000 + optional: false + description: Largest attenuation this device can be dialled to, in millidecibels. + order_weight: 1920 + - name: max_attenuation_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if max_attenuation_mdb__value is not none %}{{ max_attenuation_mdb__value / 1000 }} dB{% endif %} + optional: true + description: Maximum attenuation in dB. Read-only, derived from max_attenuation_mdb. + order_weight: 1930 + + # ---- from schemas/otn_carrier.yml ---- + - name: OpticalCarrier + namespace: Otn + description: One provisioned wavelength. Occupies its channel on every section it crosses. + label: Optical carrier + icon: mdi:waves + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: "^[A-Za-z0-9._-]+$" + optional: false + order_weight: 1000 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1100 + # A carrier the generator writes on a branch stays `planned` until the + # change merges; a pre-provisioned one is `active`. + - name: status + kind: Dropdown + default_value: active + choices: + - name: planned + label: Planned + color: "#2196f3" + - name: active + label: Active + color: "#4caf50" + - name: decommissioned + label: Decommissioned + color: "#9e9e9e" + optional: false + order_weight: 1200 + relationships: + - name: channel + peer: OtnFrequencyGrid + kind: Attribute + cardinality: one + optional: false + identifier: otn_carrier__channel + on_delete: no-action + order_weight: 800 + - name: optical_mode + peer: OtnOpticalMode + kind: Attribute + cardinality: one + optional: true + identifier: otn_carrier__optical_mode + on_delete: no-action + order_weight: 810 + # Many, not one, and the change is the grooming model showing through. A + # wavelength used to carry one service, so one optical path per carrier was + # the truth. Once two services groom their client containers into the same + # line container, both of their paths ride this carrier. Each service still + # has exactly one path of its own, which is `otn_service__optical_path` and + # is untouched. + # + # The name stays singular although the cardinality is now many, a + # deliberate ugliness rather than an oversight. See the identifier below. + - name: optical_path + peer: OtnOpticalPath + kind: Attribute + # Measured rather than assumed: with cardinality one, the second service + # to groom into a shared wavelength failed provisioning outright with + # "Node has 2 peers for otn_carrier__optical_path, maximum of 1 + # allowed". That is the schema refusing the thing the feature exists to + # do. + cardinality: many + optional: true + # Renaming to `optical_paths` is a relationship rename, and Infrahub + # reads the old and the new as two relationships on one identifier: + # "Identifier of relationships must be unique for a given direction". + # Widening a cardinality migrates cleanly, renaming does not, and the + # rename would cost a re-bootstrap of every branch to buy a plural. + identifier: otn_carrier__optical_path + on_delete: no-action + order_weight: 820 + # Unordered, and it does not need to be: occupancy is a set membership + # question. Ordering along a route lives on the path hops. + - name: sections + peer: OtnOpticalMultiplexSection + kind: Attribute + cardinality: many + optional: true + identifier: otn_carrier__sections + on_delete: no-action + order_weight: 900 + # What the wavelength carries, so "what dies if this carrier dies" is a + # read of the carrier rather than a reverse filter. + - name: containers + peer: OtnContainer + kind: Attribute + cardinality: many + optional: true + identifier: otn_carrier__containers + on_delete: no-action + order_weight: 910 + # The inverse of OtnOduSwitch.carriers, and both sides write + # `otn_odu_switch__carriers` by hand. Leaving it off would have Infrahub + # derive one per side from that side's own kind and peer, which agrees + # only when the two pairs mirror each other, and the derived string is + # frozen the moment a load succeeds. Changing it afterwards is rejected + # with `not_supported` and costs a remove-and-re-add on both sides. + # + # Attachment, not termination. A regenerator terminates the two + # wavelengths it joins; a cross-connect grooms behind a transponder and + # terminates nothing. `line_ports` is the termination answer. + - name: odu_switches + peer: OtnOduSwitch + label: ODU switches this carrier is patched to + kind: Attribute + cardinality: many + optional: true + identifier: otn_odu_switch__carriers + on_delete: no-action + order_weight: 920 + # The inverse of OtnLinePort.carrier, both sides writing + # otn_carrier__line_ports by hand, on the same argument odu_switches above + # makes: a derived identifier is frozen the moment a load succeeds and + # changing it afterwards costs a remove-and-re-add on both sides. + # + # Many, because a wavelength is terminated at each of its ends, and those + # are two ports on two devices at two sites. + # + # on_delete: no-action, and here the case is plainer than on the port side: + # cascade would mean deleting a wavelength deletes the physical ports that + # terminated it, so retiring a service would remove hardware from the + # inventory. + # + # Optional, and it is genuinely reachable: generators/optical_service.py + # provisions a wavelength and binds no line port, so a freshly provisioned + # carrier has an empty list here. + - name: line_ports + peer: OtnLinePort + label: Line ports terminating this wavelength + kind: Attribute + cardinality: many + optional: true + identifier: otn_carrier__line_ports + on_delete: no-action + order_weight: 930 + + # ---- from schemas/otn_service.yml ---- + - name: Service + namespace: Otn + description: Customer intent. Two endpoints, a rate, a profile and an optional latency budget. + label: Service + icon: mdi:file-document-outline + include_in_menu: false + # `CoreArtifactTarget` because a service is the target of a generator + # definition. + inherit_from: + - CoreArtifactTarget + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: "^[A-Za-z0-9._-]+$" + optional: false + order_weight: 1000 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1100 + - name: customer + kind: Text + parameters: + max_length: 64 + optional: false + description: Who bought it. A Text field, because this model has no organisation kind. + order_weight: 1200 + # Customer intent, not a modulation format. The generator enumerates every + # mode whose line rate meets this number; the mode it picks lands on the + # carrier. + - name: rate_gbps + kind: Number + parameters: + min_value: 1 + max_value: 1600 + optional: false + description: Requested client capacity in Gbps. Already whole in its natural unit. + order_weight: 1300 + - name: sla + kind: Dropdown + default_value: silver + choices: + - name: gold + label: Gold + color: "#ffc107" + - name: silver + label: Silver + color: "#9e9e9e" + - name: bronze + label: Bronze + color: "#795548" + - name: best_effort + label: Best effort + color: "#607d8b" + optional: false + order_weight: 1400 + # `rejected` is what the generator writes when it refuses a service. + # Without it a refusal is indistinguishable from a generator that never + # ran. + - name: status + kind: Dropdown + default_value: planned + choices: + - name: planned + label: Planned + color: "#2196f3" + - name: provisioning + label: Provisioning + color: "#ff9800" + - name: active + label: Active + color: "#4caf50" + - name: rejected + label: Rejected + color: "#f44336" + - name: decommissioned + label: Decommissioned + color: "#9e9e9e" + optional: false + order_weight: 1500 + # The first three profiles carry a latency budget, the last two leave it + # null, and the impact report filters on this. + - name: service_profile + kind: Dropdown + default_value: ip-transit + choices: + - name: ai-training-dci + label: AI training DCI + color: "#e91e63" + - name: ai-inference + label: AI inference + color: "#9c27b0" + - name: hpc-research + label: HPC research + color: "#3f51b5" + - name: ip-transit + label: IP transit + color: "#2196f3" + - name: legacy-sdh + label: Legacy SDH + color: "#795548" + optional: false + order_weight: 1600 + - name: max_latency_ns + kind: Number + parameters: + min_value: 0 + max_value: 1000000000 + optional: true + description: One-way latency budget in nanoseconds. Null on the profiles that have none. + order_weight: 1700 + - name: max_latency_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if max_latency_ns__value is not none %}{{ max_latency_ns__value / 1000 }} us{% endif %} + optional: true + description: Latency budget in microseconds. Read-only, derived from max_latency_ns. + order_weight: 1710 + # The refusal, split in two. It was one Text attribute holding + # `"{code}: {detail}"`, and the code half was a Python constant that no + # schema knew about, so a typo in it became a seventh reason nobody read + # and no write refused it. + # + # Six choices, and the colours carry information the old string could not. + # Grey means the request was unanswerable: no route between the endpoints, + # or no transponder mode that meets the rate. Red means the physics + # refused a route that exists: the optical budget or the latency budget. + # Amber means the network is full: no spectrum on the corridor, or no + # tributary slots left in the containers. An operator reading a list of + # refusals learns from the colour alone whether to re-plan, to regenerate + # or to build. + # + # Optional, because a provisioned service carries no refusal. The pairing + # of a `rejected` status with an empty code is not a state the generator + # can reach, and `checks/provisionable.py` fails closed on it rather than + # reporting green for a refusal nobody can read. + # + # `tests/unit/test_schema_contract.py` asserts these six names equal the + # six Python constants. A code added to one and not the other would + # otherwise fail at write time on a live branch and nowhere earlier. + - name: rejection_code + kind: Dropdown + choices: + - name: no-route + label: No route + description: No path exists between the two endpoints. + color: "#9e9e9e" + - name: no-mode + label: No mode + description: No transponder mode meets the requested rate. + color: "#757575" + - name: budget + label: Optical budget + description: A route exists and the OSNR margin is negative on all of them. + color: "#f44336" + - name: latency + label: Latency budget + description: A route exists and every one of them is slower than the service allows. + color: "#b71c1c" + - name: capacity + label: No spectrum + description: A route exists and no channel on it is free. + color: "#ff9800" + - name: no-slots + label: No tributary slots + description: A wavelength was found and its containers have no room for the client. + color: "#ffb74d" + optional: true + description: Why the service was refused, as one of six codes. Empty whenever a path exists. + order_weight: 1801 + # The prose half of the old string. 512 is what `rejection_reason` + # carried and what the generator already truncates to, so the cap is + # unchanged rather than newly imposed. + - name: rejection_detail + kind: Text + parameters: + max_length: 512 + optional: true + description: What the refusal looked like in detail. The prose beside the code, never parsed. + order_weight: 1802 + # Only a person ever sets this to true. It is a signature on a refusal: + # somebody read the code and the detail, decided the demo wants the + # refusal recorded rather than fixed, and let the branch merge. + # + # `optional: false` with `default_value: false` is what makes it safe to + # add to a kind that already has rows. Mandatory with no default fails + # validation against every existing service and blocks the whole schema + # update; the default lands `false` on all of them, which is the correct + # reading of every service written before this feature. + # + # The generator may only clear it, and only on the path where it + # provisions the service, because the refusal that was signed for has + # ceased to exist. While the service remains refused the generator leaves + # it alone. Both halves matter and the natural mistake is to treat it as a + # third field beside the code and the detail and clear it on every rerun, + # which silently un-accepts a decision somebody signed. + - name: refusal_accepted + kind: Boolean + default_value: false + optional: false + description: A person read the refusal and accepted it. Only a person sets this. + order_weight: 1803 + relationships: + # Peered at the generic, not at OtnRouter, so a service can terminate on a + # transponder. Both endpoints are one-sided: "which services land on this + # device" is a native filter on the service side. + - name: endpoint_a + peer: OtnGenericDevice + kind: Attribute + cardinality: one + optional: false + identifier: otn_service__endpoint_a + on_delete: no-action + order_weight: 800 + - name: endpoint_z + peer: OtnGenericDevice + kind: Attribute + cardinality: one + optional: false + identifier: otn_service__endpoint_z + on_delete: no-action + order_weight: 810 + # Many, not one, because a circuit regenerated at an intermediate site is + # one path per wavelength and each path carries its own budget. The order + # is `segment_sequence` on the path, since an Infrahub relationship hands + # back a set. + # + # The widening is not free. The GraphQL selection shape changes from + # `optical_path { node { ... } }` to `optical_path { edges { node ... } }`, + # and every query already selecting it is broken from the moment this + # loads until it is migrated. Three were: service_trace.gql, + # service_latency.gql and srlg_exposure.gql. span_impact.gql was NOT, + # which was measured rather than assumed: it reached optical_path from + # OtnOpticalCarrier, and feature 016 had already widened that one. + - name: optical_path + peer: OtnOpticalPath + kind: Attribute + # `schema check` cannot say any of this, because it does not execute a + # `.gql`. Feature 016 measured the failure this warns about: an + # unmigrated query fails at repository sync naming `NestedEdged`, + # a type in no schema file and no documentation. + cardinality: many + optional: true + identifier: otn_service__optical_path + on_delete: no-action + order_weight: 900 + # Optional: a service that names no client signal is still provisionable + # from its rate alone. + # + # `queries/optical_service.gql` has to select it. A relationship the query + # does not ask for is a relationship the generator cannot see, and the + # failure is silent. + - name: client_signal + peer: OtnClientSignal + kind: Attribute + cardinality: one + optional: true + identifier: otn_service__client_signal + on_delete: no-action + order_weight: 910 + # Two services pointing at the same group are declaring that their routes + # must not share a conduit. + # + # A relationship, and this node carried a Text attribute for it first, which + # is why the point is worth keeping. Two services were in one group only + # when their strings matched exactly, so `gold-pair` and `gold_pair` were + # two groups of one and the check passed both in silence. Silence is also + # what an absent group means, so nothing distinguished "no requirement + # declared" from "requirement declared and mistyped", and the second is the + # one that costs a customer a circuit. A regex narrows the ways to typo it + # and removes none. Feature 016 shipped the same mistake in different + # clothes, encoding a container's owning service in the container's name. + # + # An identifier either resolves to the group object or it fails at write + # time, so there is no near-miss for the check to pass in silence. + # + # Optional, and that is the whole of the design. A service with no group + # is not checked at all, so an exposure an operator accepted deliberately + # stays accepted and the check says nothing about it. The check flags a + # violated declaration, never a shared conduit on its own, which is what + # answers the objection `transforms/srlg_exposure.py` records about + # blocking a merge on a decision somebody already made. + - name: diversity_group + peer: OtnDiversityGroup + kind: Attribute + cardinality: one + optional: true + identifier: otn_diversity_group__services + # Matching every other cross-reference here. A group and a service are + # independent objects: deleting the group ends the requirement and must + # not delete the circuits that carried it. + on_delete: no-action + order_weight: 915 + # The client containers this circuit owns. Written from the container side, + # which is where the comment on `OtnContainer.service` explains why the + # ownership is stored rather than walked back through the carrier: a carrier + # holds many optical paths now, so the walk answers with every service on + # the wavelength. + # + # `on_delete: no-action` matches every other container relationship. A + # deleted service leaves its container behind, inert, the same way a + # reclaimed carrier leaves its line container behind. + - name: containers + peer: OtnContainer + kind: Attribute + cardinality: many + optional: true + identifier: otn_service__containers + on_delete: no-action + order_weight: 920 + + # A node, not a Text attribute and not a Dropdown, and the two rejections have + # different reasons. + # + # Not a Text attribute, because that is a naming convention encoding a link in + # a string, and the comment on the retired `OtnService.diversity_group` + # attribute above has the failure it produces. + # + # Small on purpose. Everything about which routes are actually disjoint is + # computed from the spans, so this kind holds the declaration and nothing else. + - name: DiversityGroup + namespace: Otn + # Not a `Dropdown`, because a Dropdown's choices are schema. Adding a group + # would then be a schema migration, run against loaded data, when what an + # operator is doing is writing down a promise made to a customer that + # morning. A node makes that an object write. A Dropdown also has nowhere to + # put the rest of what a group is: what it is for, who asked for it, what + # level of diversity was sold. `description` carries that, and a `Dropdown` + # choice has only a label and a colour. + description: A declared diversity requirement. The services in one group must route over disjoint conduits. + label: Diversity group + icon: mdi:call-split + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + # Unique, because the name is the identity an operator types and the + # `human_friendly_id` above resolves. Two groups of one name would make + # the reference ambiguous, which is the failure the Text attribute had. + # + # The same length and regex as every other name in this schema. Here the + # regex is cosmetic rather than load-bearing: a typo now names a group + # that does not exist and the write fails, instead of silently naming a + # new group of one. + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: "^[A-Za-z0-9._-]+$" + optional: false + order_weight: 1000 + # Why the group exists, in the operator's words. This is the field a + # string could not have carried, and it is half of why the group is a node. + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1100 + relationships: + # The inverse side, sharing one explicit identifier with + # `OtnService.diversity_group`. Both sides carry it by hand: Infrahub keys + # a relationship by its identifier rather than by the two peer kinds, the + # auto-derived form is only safe when both sides' kind-and-peer pairs + # mirror each other, and it is frozen once loaded. Two diverging + # identifiers do not error, they split into two one-way edges that read + # empty from the other side. + # + # The check reads the group's services from here, in one selection, which + # is the other half of why this is a node: the Text attribute forced a + # scan of every service to reassemble a group by string equality. + - name: services + peer: OtnService + kind: Attribute + cardinality: many + optional: true + identifier: otn_diversity_group__services + on_delete: no-action + order_weight: 800 + + - name: OpticalPath + namespace: Otn + description: The materialised chosen route for one service, with the budget that chose it. + label: Optical path + icon: mdi:map-marker-path + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + # No two segments of one circuit carry the same sequence number. The server + # refuses the second write; nothing has to notice it afterwards. + # + # The shape is a relationship plus an attribute of this node, the normal + # case, which `OtnGenericPort` already uses as `["device", "name__value"]`. + # What it could not be is `["optical_path", "segment_sequence__value"]` on + # `OtnService`: the attribute half has to belong to the node the constraint + # sits on, and `segment_sequence` belongs to the path. + # + # It needs `service` to be mandatory, which it now is. The measurement that + # allowed that is on the `service` relationship below. + uniqueness_constraints: + # It does not cover a gap. It refuses a repeat of segment 2 and is blind + # to the sequence 1, 2, 4, because a schema constrains what is written and + # cannot notice what is missing. The completeness check owns that ground, + # and why is in `docs/docs/client-mapping.mdx`. + - ["service", "segment_sequence__value"] + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: "^[A-Za-z0-9._-]+$" + optional: false + order_weight: 1000 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1100 + # Which segment of its circuit this path is. Mandatory, and unlike + # OtnAmplifier.oms_sequence it carries a default, because the kinds this + # feature touches all have live instances and a mandatory attribute with + # no default fails validation against every one of them and blocks the + # whole schema update. Not unique on its own: segment 1 exists once per + # circuit, not once in the network, and the pair with the service is the + # `uniqueness_constraints` entry above. + - name: segment_sequence + kind: Number + # 1 is not merely a value that loads. It is the correct reading of every + # path written before this feature: one segment of one. A path that was + # the whole route is segment 1, and stays it. + default_value: 1 + parameters: + min_value: 1 + # A sanity ceiling in the manner of OtnPathHop.sequence, not a derived + # bound. Tying it to the section or the site count would make the + # bound wrong the moment the plant grows. + max_value: 50 + optional: false + description: Which segment of its circuit this path is. 1 for a circuit that spans one wavelength. + order_weight: 1200 + # total_length_m, osnr_total_mdb, osnr_margin_mdb and latency_ns were + # measured over the same enumeration as total_loss_mdb below, and none of + # them needed moving. The extremes are 3450000 m against a cap of + # 10000000, OSNR 19045 to 36243 against 0 to 100000, margin -6455 to + # +16243 against -100000 to +100000, and 16902741 ns against 1000000000. + - name: total_length_m + kind: Number + parameters: + min_value: 0 + max_value: 10000000 + optional: false + description: Route length in metres, summed over every span on the path. + order_weight: 1500 + - name: total_length_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if total_length_m__value is not none %}{{ total_length_m__value / 1000 }} km{% endif %} + optional: true + description: Route length in km. Read-only, derived from total_length_m. + order_weight: 1510 + # 1000000 is 1000 dB, the measured worst case rounded up rather than a + # guess. The traversal returns routes of at most four sections, and every + # such route on the shipped plant was budgeted both ways: the largest loss + # is Madrid to Vienna at 854295, over 3450 km. The old 500000 refused that + # at write time with "854295 is higher than the maximum allowed value", + # and it already refused Madrid to Warsaw unregenerated at 737146, the + # route the regeneration scenario exists to fail. A cap that rejects a + # route the model is supposed to report as too lossy hides the negative + # result behind a write error. + - name: total_loss_mdb + kind: Number + parameters: + min_value: 0 + # A regenerated route is one path per segment, each carrying its own + # loss, so a cut lowers the figure: Madrid to Warsaw cut at Frankfurt + # is 462196 and 281950. The bound comes from the unregenerated route, + # never from a segment. + max_value: 1000000 + optional: false + description: End-to-end loss in millidecibels, ageing allowance included. + order_weight: 1600 + - name: total_loss_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if total_loss_mdb__value is not none %}{{ total_loss_mdb__value / 1000 }} dB{% endif %} + optional: true + description: End-to-end loss in dB. Read-only, derived from total_loss_mdb. + order_weight: 1610 + - name: osnr_total_mdb + kind: Number + parameters: + min_value: 0 + max_value: 100000 + optional: false + description: OSNR delivered at the receiver in millidecibels, over the amplifier cascade. + order_weight: 1700 + - name: osnr_total_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if osnr_total_mdb__value is not none %}{{ osnr_total_mdb__value / 1000 }} dB{% endif %} + optional: true + description: Delivered OSNR in dB. Read-only, derived from osnr_total_mdb. + order_weight: 1710 + - name: osnr_margin_mdb + kind: Number + parameters: + min_value: -100000 + max_value: 100000 + optional: false + description: Delivered OSNR less the mode requirement and the system margin, in millidecibels. + order_weight: 1800 + - name: osnr_margin_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if osnr_margin_mdb__value is not none %}{{ osnr_margin_mdb__value / 1000 }} dB{% endif %} + optional: true + description: Signed OSNR margin in dB. Read-only, derived from osnr_margin_mdb. + order_weight: 1810 + - name: latency_ns + kind: Number + parameters: + min_value: 0 + max_value: 1000000000 + optional: false + description: One-way latency in nanoseconds. Propagation, node, amplifier and FEC terms. + order_weight: 1900 + - name: latency_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if latency_ns__value is not none %}{{ latency_ns__value / 1000 }} us{% endif %} + optional: true + description: One-way latency in microseconds. Read-only, derived from latency_ns. + order_weight: 1910 + relationships: + # Mandatory. A path with no service has no meaning in this model: it is + # one wavelength's route chosen to answer one customer's request, and the + # figures on it were computed from that request's rate and mode. + # + # It was optional, on the stated grounds that a path is written before it + # is attached and that the generator's refusal path leaves paths behind. + # Both were measured false. `generators/optical_service.py` passes + # `"service": str(service["id"])` inside the same `client.create` call + # that makes the path, so a path never exists unattached even for one + # round trip, and feature 016 made the refusal path create nothing at all. + - name: service + peer: OtnService + kind: Attribute + cardinality: one + # The migration was measured before the change, not reasoned about: 17 + # OtnOpticalPath records across the seven branches on the instance, 0 of + # them with no service, so nothing exists for a mandatory relationship + # to fail against. Had any been found, this would have stayed optional + # and the uniqueness constraint above would not exist. See T009a. + # + # It is also what that constraint needs. A constraint naming a + # relationship is rejected while the relationship is optional, with + # `cannot use relationship, relationship must be + # mandatory`. So the mandatory flag is not the schema paying for the + # constraint: the modelling argument stands on its own and the + # constraint is what it buys. + optional: false + identifier: otn_service__optical_path + on_delete: no-action + order_weight: 800 + - name: carrier + peer: OtnOpticalCarrier + kind: Attribute + cardinality: one + optional: true + identifier: otn_carrier__optical_path + on_delete: no-action + order_weight: 810 + # `on_delete: cascade`, one of the two in the repository. The other is + # `OtnGenericDevice.ports` in `schemas/otn_base.yml`, and both are the same + # argument: a hop has no meaning apart from its path, a port has none apart + # from its device. Here the alternative is twenty-four orphans per deleted + # path. + - name: hops + peer: OtnPathHop + kind: Component + cardinality: many + optional: true + identifier: otn_path__hops + on_delete: cascade + order_weight: 900 + + - name: PathHop + namespace: Otn + description: One ordered element on a path, carrying the budget's running totals. + label: Path hop + icon: mdi:ray-vertex + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - sequence__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 96 + regex: "^[A-Za-z0-9._-]+$" + optional: false + order_weight: 1000 + - name: sequence + kind: Number + parameters: + min_value: 1 + max_value: 1000 + optional: false + description: Position along the path, counting from the endpoint A ROADM. + order_weight: 1100 + - name: cumulative_length_m + kind: Number + parameters: + min_value: 0 + max_value: 10000000 + optional: false + description: Metres travelled at the output of this element. + order_weight: 1500 + - name: cumulative_length_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if cumulative_length_m__value is not none %}{{ cumulative_length_m__value / 1000 }} km{% endif %} + optional: true + description: Kilometres travelled. Read-only, derived from cumulative_length_m. + order_weight: 1510 + # 1000000, the same cap as OtnOpticalPath.total_loss_mdb, and it has to be + # the same number rather than merely a large one. This attribute is that + # one's running total: the last hop's cumulative loss IS the route total, + # so a hop cap below the path cap refuses a write the path would accept. + # + # It was below it. T026c raised the path to 1000000 against a measured + # worst case of 854295 and left this at 500000, which refused 224 of the + # 830 route-directions the traversal can build on the shipped plant, over + # 122 distinct routes. The write failed on the hop rather than on the + # path, which is the wrong place to read the error and says nothing about + # the route being too lossy. `cumulative_length_m` and `total_length_m` + # were already paired at 10000000; only the loss pair had drifted. + - name: cumulative_loss_mdb + kind: Number + parameters: + min_value: 0 + # tests/unit/test_schema_contract.py asserts the pairing, so the next + # change to either cap fails rather than diverging in silence. + max_value: 1000000 + optional: false + description: Loss accumulated at the output of this element, in millidecibels. + order_weight: 1600 + - name: cumulative_loss_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if cumulative_loss_mdb__value is not none %}{{ cumulative_loss_mdb__value / 1000 }} dB{% endif %} + optional: true + description: Accumulated loss in dB. Read-only, derived from cumulative_loss_mdb. + order_weight: 1610 + # Optional because OSNR is undefined until the first amplifier has added + # noise to measure it against. The engine returns None for those hops. + - name: cumulative_osnr_mdb + kind: Number + parameters: + min_value: 0 + max_value: 100000 + optional: true + description: OSNR after this element, in millidecibels. Empty ahead of the first amplifier. + order_weight: 1700 + - name: cumulative_osnr_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if cumulative_osnr_mdb__value is not none %}{{ cumulative_osnr_mdb__value / 1000 }} dB{% endif %} + optional: true + description: Accumulated OSNR in dB. Empty ahead of the first amplifier. + order_weight: 1710 + - name: cumulative_delay_ns + kind: Number + parameters: + min_value: 0 + max_value: 1000000000 + optional: false + description: One-way delay at the output of this element, in nanoseconds. + order_weight: 1800 + - name: cumulative_delay_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if cumulative_delay_ns__value is not none %}{{ cumulative_delay_ns__value / 1000 }} us{% endif %} + optional: true + description: Accumulated delay in microseconds. Read-only, derived from cumulative_delay_ns. + order_weight: 1810 + relationships: + - name: path + peer: OtnOpticalPath + kind: Parent + cardinality: one + optional: false + identifier: otn_path__hops + order_weight: 800 + # One relationship covers ROADMs, amplifiers and fiber spans. There is no + # ingress or egress port beside it: the loaded plant has no port-level + # adjacency between a span and the amplifier that feeds it, so both would + # be null on every span hop and read by nothing. + - name: element + peer: OtnOpticalElement + kind: Attribute + cardinality: one + optional: false + identifier: otn_hop__element + on_delete: no-action + order_weight: 810 + + # ---- from schemas/location.yml ---- + - name: Site + namespace: Otn + description: A PoP hosting OTN equipment, or a customer site handing traffic to one. + label: OTN Site + icon: ri:building-line + # No menu_placement: it only positions a kind inside the auto-generated + # sidebar, which both this node and LocationGeneric opt out of. + include_in_menu: false + # `CoreArtifactTarget` is declared here, on the node, because `inherit_from` + # is the one thing a schema extension cannot add. An `extensions:` block can + # bolt an attribute or a relationship onto a kind it does not own; it cannot + # change what that kind inherits. So a site becomes an artifact target in + # this file or not at all. + # + # It is a core generic, not a vendored one, so it adds no fourth divergence + # to the three listed at the top: those are all against `LocationGeneric`, + # and `OtnSite` is a wholly local node. Two flat generics, neither + # inheriting the other. + inherit_from: + - LocationGeneric + - CoreArtifactTarget + parent: "" + children: "" + display_label: name__value + attributes: + - name: latitude_microdeg + kind: Number + parameters: + min_value: -90000000 + max_value: 90000000 + optional: true + description: Latitude in millionths of a degree. No Float kind exists. + order_weight: 1400 + - name: longitude_microdeg + kind: Number + parameters: + min_value: -180000000 + max_value: 180000000 + optional: true + description: Longitude in millionths of a degree. No Float kind exists. + order_weight: 1500 + # `customer` is a discriminator, not a label: the guard in + # `tests/unit/test_geant_dataset.py` reads it to refuse an `oms` on any + # span touching a customer site, which is what keeps a coarse metro tail + # out of the budget engine and its 1550 nm coefficient. Full reasoning in + # `docs/docs/schema-reference.mdx`, "site_type keeps the PoPs a + # filter". + - name: site_type + kind: Dropdown + default_value: pop + choices: + - name: pop + label: PoP + color: "#2196f3" + - name: customer + label: Customer site + color: "#ff9800" + optional: false + description: >- + A PoP or a customer site. Keeps "the fourteen PoPs" a filter rather than a + count. No span on a customer site may declare an oms. + order_weight: 1600 + # Four inverses, each reusing the identifier its forward side declares, + # which is what makes them read the edges already in the graph rather than + # an empty list. The forward sides are OtnGenericDevice.site in + # otn_base.yml, OtnFiberSpan.site_a and .site_b in otn_plant.yml, and + # OtnFacility.site below. + # + # The two span lists cannot be merged into one: merging the identifiers + # would lose which end a span terminates on. The labels are what make the + # union readable. + relationships: + # Cardinality one, and that is a decision. Six PoPs host one facility + # each and none hosts two, so one is the honest reading of the network as + # modelled. Widening to many later is a migration a reader can see + # coming; narrowing from many to one is not, because it throws data away. + - name: facility + peer: OtnFacility + kind: Attribute + cardinality: one + optional: true + identifier: otn_site__facility + on_delete: no-action + order_weight: 930 + - name: devices + peer: OtnGenericDevice + kind: Generic + cardinality: many + optional: true + identifier: otn_site__devices + on_delete: no-action + order_weight: 900 + - name: spans_a + peer: OtnFiberSpan + label: Fiber spans (A end) + kind: Attribute + cardinality: many + optional: true + identifier: otn_span__site_a + on_delete: no-action + order_weight: 910 + - name: spans_b + peer: OtnFiberSpan + label: Fiber spans (B end) + kind: Attribute + cardinality: many + optional: true + identifier: otn_span__site_b + on_delete: no-action + order_weight: 920 + # Six EuroHPC facilities sit on six of the fourteen PoPs. Until this kind + # existed the only record of that was the text after `eurohpc-` in a + # BuiltinTag name, read back by a helper duplicated in two transforms. The + # tag read was defended on a distinction that still holds: it read a tag, not + # a device, and nothing about an amplifier, a ROADM or a router is recovered + # from a name anywhere in this repository. + # + # The reversal is about something else. The failure was silent in both + # directions, measured on a probe branch: renaming `eurohpc-vega` dropped + # Vega from both maps and raised nothing, and a mistyped `eurohpc_deucalion` + # made a facility no map draws and no check reports. An edge fails loudly. + - name: Facility + namespace: Otn + description: A supercomputing facility hosted at a PoP. + label: Facility + icon: ri:cpu-line + # Reached through the site it sits on, like every other kind here, so it + # stays out of the automatic sidebar. menus/otn.yml carries the entries. + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + # The tag suffix, and it has to stay the tag suffix: mapchrome.py + # upper-cases this string for the caption and the committed golden holds + # the upper-cased suffix. So `marenostrum-5`, never `MareNostrum 5`. + - name: name + kind: Text + unique: true + order_weight: 1000 + # What a tag could never carry, and half the reason this is a kind. + - name: description + kind: Text + optional: true + order_weight: 1200 + # The six `eurohpc-` tags stay on their sites. They are data an operator + # wrote and a sweep does not delete data. They stop being read, which was + # the whole finding. + relationships: + - name: site + peer: OtnSite + kind: Attribute + cardinality: one + optional: true + identifier: otn_site__facility + on_delete: no-action + order_weight: 900 From 281b565e5ac9dc1599668c89ce6a2e5a22c78210 Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 11:06:28 +0200 Subject: [PATCH 03/31] fix(otn): reword OduSwitch description, restore column-0 comments, tidy metadata --- .metadata.yml | 4 +--- extensions/otn/otn.yml | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.metadata.yml b/.metadata.yml index fd2f9140..96884bfc 100644 --- a/.metadata.yml +++ b/.metadata.yml @@ -226,9 +226,7 @@ extensions/otn: - base - extensions/location_site description: | - Optical transport network schemas covering the physical plant, the - wavelength catalog, optical devices and their ports, pluggable optics, - carriers and end-to-end services. + Optical transport network schemas covering the physical plant, the wavelength catalog, optical devices and their ports, pluggable optics, carriers and end-to-end services. name: OTN extensions/patch_panel: dependencies: diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index 4aeed478..d47a7b65 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -13,6 +13,9 @@ version: "1.0" generics: # ---- from schemas/otn_base.yml ---- + # None of them inherits from another. Infrahub's GenericSchemaWrite has no + # inherit_from key at all, so a generic taxonomy is composed by listing several + # generics on a concrete node, never by stacking them. - name: GenericPort namespace: Otn description: Any port on any OTN device. The connection surface of the network. @@ -485,6 +488,14 @@ generics: order_weight: 1900 # ---- from schemas/otn_ports.yml ---- + # Two flat generics. Generics cannot inherit generics in Infrahub, so the + # concrete monitors multi-inherit both plus OtnGenericPort, which is the shape + # OtnAmplifierPort already uses for OtnGenericPort and OtnOpticalPort. + # + # human_friendly_id and uniqueness_constraints are not declared here. Both come + # from OtnGenericPort, and a constraint on a generic binds across every kind + # that inherits it, so (device, name) stays unique across all five monitors + # taken together. - name: Monitor namespace: Otn description: The one fact every monitoring interface carries, whatever it measures. @@ -1410,6 +1421,14 @@ nodes: order_weight: 950 # ---- from schemas/otn_plant.yml ---- + # A `cardinality: one` relationship here gets an inverse when the peer is a page + # an operator lands on and the reverse list is what that page is for. Otherwise + # the reverse direction is a server-side filter on the forward side, which costs + # nothing and does not duplicate the fact. site_a and site_b have inverses on + # OtnSite in location.yml; roadm_a and roadm_b have inverses on OtnRoadm in + # otn_devices.yml. Every inverse reuses the identifier declared here, and an + # inverse on a new identifier loads without complaint and then reads an empty + # list. - name: FiberType namespace: Otn description: Single-mode fiber family. Attenuation, dispersion and group index at 1550 nm. @@ -2953,8 +2972,7 @@ nodes: - name: OduSwitch namespace: Otn - description: O-E-O device. Terminates one wavelength and originates the next, as a - regenerator or an ODU cross-connect. + description: O-E-O device. Terminates one wavelength, originates the next, as a regenerator or ODU cross-connect. label: ODU switch icon: mdi:swap-horizontal-variant include_in_menu: false From af0ace7999fcf77896d7e2a19fa19225e30f7afa Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 11:18:55 +0200 Subject: [PATCH 04/31] feat(otn): reuse LocationSite instead of a second site kind --- extensions/otn/otn.yml | 194 +++++++++++++++++------------------------ 1 file changed, 79 insertions(+), 115 deletions(-) diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index d47a7b65..c123999b 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -289,7 +289,7 @@ generics: # Not a Parent: Parent would force optional: false, and a device has to be # creatable before its site record exists. - name: site - peer: OtnSite + peer: LocationSite kind: Attribute cardinality: one optional: true @@ -1733,7 +1733,7 @@ nodes: on_delete: no-action order_weight: 800 - name: site_a - peer: OtnSite + peer: LocationSite kind: Attribute cardinality: one optional: false @@ -1741,7 +1741,7 @@ nodes: on_delete: no-action order_weight: 810 - name: site_b - peer: OtnSite + peer: LocationSite kind: Attribute cardinality: one optional: false @@ -4191,117 +4191,6 @@ nodes: on_delete: no-action order_weight: 810 - # ---- from schemas/location.yml ---- - - name: Site - namespace: Otn - description: A PoP hosting OTN equipment, or a customer site handing traffic to one. - label: OTN Site - icon: ri:building-line - # No menu_placement: it only positions a kind inside the auto-generated - # sidebar, which both this node and LocationGeneric opt out of. - include_in_menu: false - # `CoreArtifactTarget` is declared here, on the node, because `inherit_from` - # is the one thing a schema extension cannot add. An `extensions:` block can - # bolt an attribute or a relationship onto a kind it does not own; it cannot - # change what that kind inherits. So a site becomes an artifact target in - # this file or not at all. - # - # It is a core generic, not a vendored one, so it adds no fourth divergence - # to the three listed at the top: those are all against `LocationGeneric`, - # and `OtnSite` is a wholly local node. Two flat generics, neither - # inheriting the other. - inherit_from: - - LocationGeneric - - CoreArtifactTarget - parent: "" - children: "" - display_label: name__value - attributes: - - name: latitude_microdeg - kind: Number - parameters: - min_value: -90000000 - max_value: 90000000 - optional: true - description: Latitude in millionths of a degree. No Float kind exists. - order_weight: 1400 - - name: longitude_microdeg - kind: Number - parameters: - min_value: -180000000 - max_value: 180000000 - optional: true - description: Longitude in millionths of a degree. No Float kind exists. - order_weight: 1500 - # `customer` is a discriminator, not a label: the guard in - # `tests/unit/test_geant_dataset.py` reads it to refuse an `oms` on any - # span touching a customer site, which is what keeps a coarse metro tail - # out of the budget engine and its 1550 nm coefficient. Full reasoning in - # `docs/docs/schema-reference.mdx`, "site_type keeps the PoPs a - # filter". - - name: site_type - kind: Dropdown - default_value: pop - choices: - - name: pop - label: PoP - color: "#2196f3" - - name: customer - label: Customer site - color: "#ff9800" - optional: false - description: >- - A PoP or a customer site. Keeps "the fourteen PoPs" a filter rather than a - count. No span on a customer site may declare an oms. - order_weight: 1600 - # Four inverses, each reusing the identifier its forward side declares, - # which is what makes them read the edges already in the graph rather than - # an empty list. The forward sides are OtnGenericDevice.site in - # otn_base.yml, OtnFiberSpan.site_a and .site_b in otn_plant.yml, and - # OtnFacility.site below. - # - # The two span lists cannot be merged into one: merging the identifiers - # would lose which end a span terminates on. The labels are what make the - # union readable. - relationships: - # Cardinality one, and that is a decision. Six PoPs host one facility - # each and none hosts two, so one is the honest reading of the network as - # modelled. Widening to many later is a migration a reader can see - # coming; narrowing from many to one is not, because it throws data away. - - name: facility - peer: OtnFacility - kind: Attribute - cardinality: one - optional: true - identifier: otn_site__facility - on_delete: no-action - order_weight: 930 - - name: devices - peer: OtnGenericDevice - kind: Generic - cardinality: many - optional: true - identifier: otn_site__devices - on_delete: no-action - order_weight: 900 - - name: spans_a - peer: OtnFiberSpan - label: Fiber spans (A end) - kind: Attribute - cardinality: many - optional: true - identifier: otn_span__site_a - on_delete: no-action - order_weight: 910 - - name: spans_b - peer: OtnFiberSpan - label: Fiber spans (B end) - kind: Attribute - cardinality: many - optional: true - identifier: otn_span__site_b - on_delete: no-action - order_weight: 920 # Six EuroHPC facilities sit on six of the fourteen PoPs. Until this kind # existed the only record of that was the text after `eurohpc-` in a # BuiltinTag name, read back by a helper duplicated in two transforms. The @@ -4344,10 +4233,85 @@ nodes: # the whole finding. relationships: - name: site - peer: OtnSite + peer: LocationSite kind: Attribute cardinality: one optional: true identifier: otn_site__facility on_delete: no-action order_weight: 900 + +# LocationSite carries the OTN site fields rather than a second site kind. Three +# of OtnSite's attributes and four of its relationships move here. Its +# `facility` relationship is renamed `otn_facility`, because LocationSite +# already has a `facility` Text attribute and a node cannot hold both under one +# name. Its `CoreArtifactTarget` inheritance is dropped, because an extensions +# block cannot add to inherit_from and this library ships no generators. +extensions: + nodes: + - kind: LocationSite + attributes: + - name: latitude_microdeg + kind: Number + parameters: + min_value: -90000000 + max_value: 90000000 + optional: true + description: Latitude in millionths of a degree. No Float kind exists. + order_weight: 1400 + - name: longitude_microdeg + kind: Number + parameters: + min_value: -180000000 + max_value: 180000000 + optional: true + description: Longitude in millionths of a degree. No Float kind exists. + order_weight: 1500 + - name: site_type + kind: Dropdown + default_value: pop + choices: + - name: pop + label: PoP + color: "#2196f3" + - name: customer + label: Customer site + color: "#ff9800" + optional: false + description: A PoP hosting OTN equipment, or a customer site handing traffic to one. + order_weight: 1600 + relationships: + - name: otn_facility + peer: OtnFacility + kind: Attribute + cardinality: one + optional: true + identifier: otn_site__facility + on_delete: no-action + order_weight: 930 + - name: devices + peer: OtnGenericDevice + kind: Generic + cardinality: many + optional: true + identifier: otn_site__devices + on_delete: no-action + order_weight: 900 + - name: spans_a + peer: OtnFiberSpan + label: Fiber spans (A end) + kind: Attribute + cardinality: many + optional: true + identifier: otn_span__site_a + on_delete: no-action + order_weight: 910 + - name: spans_b + peer: OtnFiberSpan + label: Fiber spans (B end) + kind: Attribute + cardinality: many + optional: true + identifier: otn_span__site_b + on_delete: no-action + order_weight: 920 From cec2e71b16e0911c349b5fd8bf38b7df06b81832 Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 11:24:55 +0200 Subject: [PATCH 05/31] fix(otn): correct the otn_plant.yml preamble's stale OtnSite and file references --- extensions/otn/otn.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index c123999b..388f630a 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -1425,10 +1425,10 @@ nodes: # an operator lands on and the reverse list is what that page is for. Otherwise # the reverse direction is a server-side filter on the forward side, which costs # nothing and does not duplicate the fact. site_a and site_b have inverses on - # OtnSite in location.yml; roadm_a and roadm_b have inverses on OtnRoadm in - # otn_devices.yml. Every inverse reuses the identifier declared here, and an - # inverse on a new identifier loads without complaint and then reads an empty - # list. + # LocationSite, added through the `extensions:` block at the end of this file; + # roadm_a and roadm_b have inverses on OtnRoadm, defined further down in this + # same file. Every inverse reuses the identifier declared here, and an inverse + # on a new identifier loads without complaint and then reads an empty list. - name: FiberType namespace: Otn description: Single-mode fiber family. Attenuation, dispersion and group index at 1550 nm. From 7e3ec2079fe817039936acbedc749ce3422f5c95 Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 11:30:45 +0200 Subject: [PATCH 06/31] feat(otn): link an OTN device to its Dcim physical record --- extensions/otn/otn.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index 388f630a..68f606ee 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -296,6 +296,22 @@ generics: identifier: otn_site__devices on_delete: no-action order_weight: 800 + # An OTN device names the optical role; DcimPhysicalDevice names the + # physical asset. This edge joins the two records for one box, so a + # deployment that already tracks its routers in Dcim links them rather + # than duplicating them. Optional, because a deployment with no Dcim + # inventory is equally valid. It does not merge the two graphs: no OTN + # port is a DcimEndpoint, so extensions/cable still cannot terminate on + # optical gear, and an extensions block cannot change that. + - name: dcim_device + peer: DcimPhysicalDevice + label: Physical device record + kind: Attribute + cardinality: one + optional: true + identifier: otn_device__dcim_device + on_delete: no-action + order_weight: 940 - name: ports peer: OtnGenericPort kind: Component From 277ae8c67f9e87d3e71e95a2f230d36d1b3f9c4a Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 11:45:18 +0200 Subject: [PATCH 07/31] refactor(otn): declare element_class once and even out the port enums --- extensions/otn/otn.yml | 416 ++--------------------------------------- 1 file changed, 17 insertions(+), 399 deletions(-) diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index 68f606ee..64f5f4e3 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -494,12 +494,21 @@ generics: description: Nominal impedance in ohms. 120 for E1 pair, 75 for coax. order_weight: 1700 - name: connector_type - kind: Text + kind: Dropdown default_value: RJ48 - enum: - - BNC - - RJ48 - - RJ45 + choices: + - name: BNC + label: BNC + description: Coaxial bayonet connector, used for 75 ohm E1 and STM-1. + color: "#ff9800" + - name: RJ48 + label: RJ48 + description: Eight-position jack for 120 ohm balanced E1 and T1 pairs. + color: "#2196f3" + - name: RJ45 + label: RJ45 + description: Eight-position jack for Ethernet management and console. + color: "#4caf50" optional: false order_weight: 1900 @@ -1351,15 +1360,15 @@ nodes: optional: false description: Whether the laser can be tuned across the grid or is fixed at one wavelength. order_weight: 1400 - # Mandatory: a part that runs no mode says nothing about what it can do, and - # transceiver_mode_support has nothing to compare the carrier against. + # Optional, so a part number can be recorded before the mode catalog is + # populated. A type with no modes says nothing about what it can run. relationships: - name: supported_modes peer: OtnOpticalMode label: Modes this part can run kind: Attribute cardinality: many - optional: false + optional: true identifier: otn_optical_mode__transceiver_types on_delete: no-action order_weight: 900 @@ -1601,55 +1610,11 @@ nodes: order_by: - name__value display_label: name__value - # element_class is restated to give it a default_value, and the ten choices - # come with it because overriding an inherited Dropdown requires the full - # list. Omitting the `choices` key is rejected at load; a list that is one - # choice short is accepted in silence and only fails when an object writes - # the missing value. tests/unit/test_schema_contract.py holds every - # restatement to the generic's list. - # # The inherited insertion_loss_mdb stays at its default of 0 on a span. A # span's real loss is length x attenuation plus splices plus connectors plus # aging margin, and the budget engine computes it on demand. The stored 0 # does not mean the span is lossless. attributes: - - name: element_class - kind: Dropdown - default_value: fiber_span - choices: - - name: transponder - label: Transponder - color: "#2196f3" - - name: roadm - label: ROADM - color: "#9c27b0" - - name: amplifier - label: Amplifier - color: "#ff9800" - - name: mux_demux - label: Mux/demux - color: "#00bcd4" - - name: patch_panel - label: Patch panel - color: "#9e9e9e" - - name: fiber_span - label: Fiber span - color: "#4caf50" - - name: splitter - label: Splitter - color: "#8bc34a" - - name: attenuator - label: Attenuator - color: "#795548" - - name: raman_pump - label: Raman pump - color: "#e91e63" - - name: odu_switch - label: ODU switch - color: "#673ab7" - optional: false - description: What the optical budget engine branches on. - order_weight: 1800 - name: name kind: Text unique: true @@ -2483,44 +2448,6 @@ nodes: inherit_from: - OtnGenericDevice - OtnOpticalElement - attributes: - - name: element_class - kind: Dropdown - default_value: transponder - choices: - - name: transponder - label: Transponder - color: "#2196f3" - - name: roadm - label: ROADM - color: "#9c27b0" - - name: amplifier - label: Amplifier - color: "#ff9800" - - name: mux_demux - label: Mux/demux - color: "#00bcd4" - - name: patch_panel - label: Patch panel - color: "#9e9e9e" - - name: fiber_span - label: Fiber span - color: "#4caf50" - - name: splitter - label: Splitter - color: "#8bc34a" - - name: attenuator - label: Attenuator - color: "#795548" - - name: raman_pump - label: Raman pump - color: "#e91e63" - - name: odu_switch - label: ODU switch - color: "#673ab7" - optional: false - description: What the optical budget engine branches on. - order_weight: 1800 - name: Roadm namespace: Otn @@ -2531,44 +2458,6 @@ nodes: inherit_from: - OtnGenericDevice - OtnOpticalElement - attributes: - - name: element_class - kind: Dropdown - default_value: roadm - choices: - - name: transponder - label: Transponder - color: "#2196f3" - - name: roadm - label: ROADM - color: "#9c27b0" - - name: amplifier - label: Amplifier - color: "#ff9800" - - name: mux_demux - label: Mux/demux - color: "#00bcd4" - - name: patch_panel - label: Patch panel - color: "#9e9e9e" - - name: fiber_span - label: Fiber span - color: "#4caf50" - - name: splitter - label: Splitter - color: "#8bc34a" - - name: attenuator - label: Attenuator - color: "#795548" - - name: raman_pump - label: Raman pump - color: "#e91e63" - - name: odu_switch - label: ODU switch - color: "#673ab7" - optional: false - description: What the optical budget engine branches on. - order_weight: 1800 # The inverses of OtnOpticalMultiplexSection.roadm_a and .roadm_b, each on # the identifier its forward side declares. A ROADM inherits site at 800 and # ports at 900, so these follow at 960 and 970. @@ -2607,43 +2496,6 @@ nodes: # whatever sits immediately before it. OtnFiberSpan carries an explicit # position for the same reason. attributes: - - name: element_class - kind: Dropdown - default_value: amplifier - choices: - - name: transponder - label: Transponder - color: "#2196f3" - - name: roadm - label: ROADM - color: "#9c27b0" - - name: amplifier - label: Amplifier - color: "#ff9800" - - name: mux_demux - label: Mux/demux - color: "#00bcd4" - - name: patch_panel - label: Patch panel - color: "#9e9e9e" - - name: fiber_span - label: Fiber span - color: "#4caf50" - - name: splitter - label: Splitter - color: "#8bc34a" - - name: attenuator - label: Attenuator - color: "#795548" - - name: raman_pump - label: Raman pump - color: "#e91e63" - - name: odu_switch - label: ODU switch - color: "#673ab7" - optional: false - description: What the optical budget engine branches on. - order_weight: 1800 # 3000 is the quantum-limited floor for an EDFA and 10000 is a bad one, so # the bounds bracket the whole plausible range rather than this dataset's. - name: noise_figure_mdb @@ -2753,44 +2605,6 @@ nodes: inherit_from: - OtnGenericDevice - OtnOpticalElement - attributes: - - name: element_class - kind: Dropdown - default_value: mux_demux - choices: - - name: transponder - label: Transponder - color: "#2196f3" - - name: roadm - label: ROADM - color: "#9c27b0" - - name: amplifier - label: Amplifier - color: "#ff9800" - - name: mux_demux - label: Mux/demux - color: "#00bcd4" - - name: patch_panel - label: Patch panel - color: "#9e9e9e" - - name: fiber_span - label: Fiber span - color: "#4caf50" - - name: splitter - label: Splitter - color: "#8bc34a" - - name: attenuator - label: Attenuator - color: "#795548" - - name: raman_pump - label: Raman pump - color: "#e91e63" - - name: odu_switch - label: ODU switch - color: "#673ab7" - optional: false - description: What the optical budget engine branches on. - order_weight: 1800 relationships: # The wavelengths a coarse multiplexer lights are a property of the device # rather than of the plan. Without this relationship OtnCwdmChannel is a @@ -2814,44 +2628,6 @@ nodes: inherit_from: - OtnGenericDevice - OtnOpticalElement - attributes: - - name: element_class - kind: Dropdown - default_value: patch_panel - choices: - - name: transponder - label: Transponder - color: "#2196f3" - - name: roadm - label: ROADM - color: "#9c27b0" - - name: amplifier - label: Amplifier - color: "#ff9800" - - name: mux_demux - label: Mux/demux - color: "#00bcd4" - - name: patch_panel - label: Patch panel - color: "#9e9e9e" - - name: fiber_span - label: Fiber span - color: "#4caf50" - - name: splitter - label: Splitter - color: "#8bc34a" - - name: attenuator - label: Attenuator - color: "#795548" - - name: raman_pump - label: Raman pump - color: "#e91e63" - - name: odu_switch - label: ODU switch - color: "#673ab7" - optional: false - description: What the optical budget engine branches on. - order_weight: 1800 - name: RamanPump namespace: Otn @@ -2873,43 +2649,6 @@ nodes: - OtnGenericDevice - OtnOpticalElement attributes: - - name: element_class - kind: Dropdown - default_value: raman_pump - choices: - - name: transponder - label: Transponder - color: "#2196f3" - - name: roadm - label: ROADM - color: "#9c27b0" - - name: amplifier - label: Amplifier - color: "#ff9800" - - name: mux_demux - label: Mux/demux - color: "#00bcd4" - - name: patch_panel - label: Patch panel - color: "#9e9e9e" - - name: fiber_span - label: Fiber span - color: "#4caf50" - - name: splitter - label: Splitter - color: "#8bc34a" - - name: attenuator - label: Attenuator - color: "#795548" - - name: raman_pump - label: Raman pump - color: "#e91e63" - - name: odu_switch - label: ODU switch - color: "#673ab7" - optional: false - description: What the optical budget engine branches on. - order_weight: 1800 # On-off gain: the difference in received signal power with the pump on # against the pump off. It reduces the effective loss of the span it sits # on rather than adding a stage to the chain, which is why the budget @@ -3005,54 +2744,7 @@ nodes: inherit_from: - OtnGenericDevice - OtnOpticalElement - # element_class defaults to `odu_switch`, the tenth choice, added when the - # first object forced the decision. None of the original nine names an O-E-O - # device, so the alternative was defaulting to `transponder` or `roadm`, - # which puts a false value on the one attribute that says what a device is. - # Nothing in the repository reads element_class, so a wrong label costs - # nothing mechanically and is simply untrue in the data, which is the whole - # argument for paying for the tenth choice instead. attributes: - # The list now lives in eleven blocks, enumerated in otn_base.yml. A block - # missed is a dropdown offering different options depending on the kind you - # look at, and the server accepts that in silence. - - name: element_class - kind: Dropdown - default_value: odu_switch - choices: - - name: transponder - label: Transponder - color: "#2196f3" - - name: roadm - label: ROADM - color: "#9c27b0" - - name: amplifier - label: Amplifier - color: "#ff9800" - - name: mux_demux - label: Mux/demux - color: "#00bcd4" - - name: patch_panel - label: Patch panel - color: "#9e9e9e" - - name: fiber_span - label: Fiber span - color: "#4caf50" - - name: splitter - label: Splitter - color: "#8bc34a" - - name: attenuator - label: Attenuator - color: "#795548" - - name: raman_pump - label: Raman pump - color: "#e91e63" - - name: odu_switch - label: ODU switch - color: "#673ab7" - optional: false - description: What the optical budget engine branches on. - order_weight: 1800 # `regenerator` is the default because it is the cheaper device and the # one a route that does not close needs: it carries the whole payload # across without looking inside it. `cross_connect` demultiplexes to @@ -3147,43 +2839,6 @@ nodes: - OtnGenericDevice - OtnOpticalElement attributes: - - name: element_class - kind: Dropdown - default_value: attenuator - choices: - - name: transponder - label: Transponder - color: "#2196f3" - - name: roadm - label: ROADM - color: "#9c27b0" - - name: amplifier - label: Amplifier - color: "#ff9800" - - name: mux_demux - label: Mux/demux - color: "#00bcd4" - - name: patch_panel - label: Patch panel - color: "#9e9e9e" - - name: fiber_span - label: Fiber span - color: "#4caf50" - - name: splitter - label: Splitter - color: "#8bc34a" - - name: attenuator - label: Attenuator - color: "#795548" - - name: raman_pump - label: Raman pump - color: "#e91e63" - - name: odu_switch - label: ODU switch - color: "#673ab7" - optional: false - description: What the optical budget engine branches on. - order_weight: 1800 - name: attenuation_mdb kind: Number default_value: 0 @@ -3214,43 +2869,6 @@ nodes: - OtnGenericDevice - OtnOpticalElement attributes: - - name: element_class - kind: Dropdown - default_value: attenuator - choices: - - name: transponder - label: Transponder - color: "#2196f3" - - name: roadm - label: ROADM - color: "#9c27b0" - - name: amplifier - label: Amplifier - color: "#ff9800" - - name: mux_demux - label: Mux/demux - color: "#00bcd4" - - name: patch_panel - label: Patch panel - color: "#9e9e9e" - - name: fiber_span - label: Fiber span - color: "#4caf50" - - name: splitter - label: Splitter - color: "#8bc34a" - - name: attenuator - label: Attenuator - color: "#795548" - - name: raman_pump - label: Raman pump - color: "#e91e63" - - name: odu_switch - label: ODU switch - color: "#673ab7" - optional: false - description: What the optical budget engine branches on. - order_weight: 1800 - name: attenuation_mdb kind: Number default_value: 0 From f3d730b8f64f19bd485c6cbaf504234a44aeab32 Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 11:54:54 +0200 Subject: [PATCH 08/31] docs(otn): fix comments left stale by the element_class and connector_type cleanup --- extensions/otn/otn.yml | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index 64f5f4e3..d7c7a6bf 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -182,14 +182,6 @@ generics: max_length: 64 optional: true order_weight: 1700 - # Adding a choice here is not enough. Every kind that overrides this - # attribute restates the whole list, and the server accepts a restatement - # that is one choice short without a word; the divergence surfaces only - # when an object writes the missing value. The tenth choice, `odu_switch`, - # cost nine blocks and the list now lives in eleven: this one, the nine - # overrides in otn_devices.yml, and the span's in otn_plant.yml. The pinned - # list in `tests/unit/test_schema_contract.py` is the only thing that will - # tell you a block was missed. - name: element_class kind: Dropdown choices: @@ -396,8 +388,9 @@ generics: # generic restate the list in otn_ports.yml, and a restatement one choice # short is accepted in silence. # - # OtnCopperPort keeps its own connector_type as Text; no kind inherits both - # port generics. + # OtnCopperPort declares its own connector_type too, also a Dropdown, but + # with its own three-choice copper list. No kind inherits both port + # generics. - name: connector_type kind: Dropdown choices: @@ -1360,9 +1353,9 @@ nodes: optional: false description: Whether the laser can be tuned across the grid or is fixed at one wavelength. order_weight: 1400 - # Optional, so a part number can be recorded before the mode catalog is - # populated. A type with no modes says nothing about what it can run. relationships: + # Optional, so a part number can be recorded before the mode catalog is + # populated. A type with no modes says nothing about what it can run. - name: supported_modes peer: OtnOpticalMode label: Modes this part can run From d66f5af334a7826ce34e0fbf96dcaca365e0a282 Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 12:12:00 +0200 Subject: [PATCH 09/31] docs(otn): bring the comments down to library density --- extensions/otn/otn.yml | 987 +++++++++-------------------------------- 1 file changed, 211 insertions(+), 776 deletions(-) diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index d7c7a6bf..afab0497 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -12,10 +12,8 @@ version: "1.0" generics: - # ---- from schemas/otn_base.yml ---- - # None of them inherits from another. Infrahub's GenericSchemaWrite has no - # inherit_from key at all, so a generic taxonomy is composed by listing several - # generics on a concrete node, never by stacking them. + # Base generics. Generics cannot inherit generics, so a concrete node lists + # several rather than stacking them. - name: GenericPort namespace: Otn description: Any port on any OTN device. The connection surface of the network. @@ -124,11 +122,8 @@ generics: optional: false identifier: otn_device__ports order_weight: 800 - # Declared once. The relationship is symmetric and self-referencing, so - # both ends are the same edge; the explicit identifier is what stops - # Infrahub deriving a different string per side and splitting it into two - # phantom one-way links. Immutable after the first load: changing the - # identifier would orphan every connection already recorded. + # Symmetric and self-referencing, so both ends are the same edge. The explicit + # identifier stops Infrahub splitting it into two one-way links. - name: connected_to peer: OtnGenericPort kind: Attribute @@ -144,12 +139,8 @@ generics: label: Optical element icon: mdi:blur-linear include_in_menu: false - # No order_by and no human_friendly_id. `order_by: [name__value]` is - # rejected at load with "OtnOpticalElement.order_by: attribute 'name' not - # defined on this schema": the key resolves against the generic's own - # attributes, not against what its members inherit, and this generic - # declares none. So a hop's element is written by UUID and no object file - # can name one. Every member kind carries its own order_by. + # No order_by and no human_friendly_id: both resolve against this generic's own + # attributes, and it declares none. Every member kind carries its own order_by. attributes: - name: insertion_loss_mdb kind: Number @@ -288,13 +279,10 @@ generics: identifier: otn_site__devices on_delete: no-action order_weight: 800 - # An OTN device names the optical role; DcimPhysicalDevice names the - # physical asset. This edge joins the two records for one box, so a - # deployment that already tracks its routers in Dcim links them rather - # than duplicating them. Optional, because a deployment with no Dcim - # inventory is equally valid. It does not merge the two graphs: no OTN - # port is a DcimEndpoint, so extensions/cable still cannot terminate on - # optical gear, and an extensions block cannot change that. + # An OTN device names the optical role; DcimPhysicalDevice names the physical + # asset. This edge joins the two records for one box rather than duplicating + # it, and it does not merge the two graphs: no OTN port is a DcimEndpoint, so + # extensions/cable still cannot terminate on optical gear. - name: dcim_device peer: DcimPhysicalDevice label: Physical device record @@ -320,9 +308,7 @@ generics: icon: mdi:lightbulb-on-outline include_in_menu: false attributes: - # Optional because a grey router port is not on the C-band grid. The - # bounds are the two endpoints of the 96-channel 50 GHz grid in units.py, - # asserted equal by tests/unit/test_schema_contract.py. + # Optional because a grey router port is not on the C-band grid. - name: center_frequency_mhz kind: Number parameters: @@ -377,20 +363,9 @@ generics: optional: true description: Receiver sensitivity in dBm. Derived from rx_sensitivity_mdbm. order_weight: 1810 - # A Dropdown rather than an enum, so a connector carries a label, a colour - # and a sentence. The six original names are unchanged, so no loaded value - # is rewritten; MU, CS and splice are new. - # - # Changing the kind on this generic alone is refused with - # "connector_type inherited from OtnOpticalPort must be the same kind - # ["Dropdown", "Text"]", because the loaded schema has materialised the Text - # attribute onto every optical port kind. All six kinds that inherit this - # generic restate the list in otn_ports.yml, and a restatement one choice - # short is accepted in silence. - # - # OtnCopperPort declares its own connector_type too, also a Dropdown, but - # with its own three-choice copper list. No kind inherits both port - # generics. + # A Dropdown rather than an enum, so a connector carries a label, a colour and + # a sentence. Every kind inheriting this generic restates the list, and a + # restatement one choice short is accepted in silence. - name: connector_type kind: Dropdown choices: @@ -432,9 +407,8 @@ generics: color: "#9e9e9e" optional: true order_weight: 1900 - # Return loss is a polish property, not a connector property: an LC comes - # in all three. Null on every port loaded before this attribute existed, - # which is what keeps the change non-destructive. + # Return loss is a polish property, not a connector property: an LC comes in + # all three. Optional, so ports loaded before it existed stay valid. - name: polish kind: Dropdown choices: @@ -465,9 +439,7 @@ generics: icon: mdi:cable-data include_in_menu: false attributes: - # kbps, not Mbps. E1 is 2.048 Mbps and T1 is 1.544 Mbps; as integer Mbps - # both round to 2, which would make the two signals this generic exists to - # carry indistinguishable. + # kbps, not Mbps: as integer Mbps, E1 at 2.048 and T1 at 1.544 both round to 2. - name: speed_kbps kind: Number default_value: 2048 @@ -505,15 +477,8 @@ generics: optional: false order_weight: 1900 - # ---- from schemas/otn_ports.yml ---- - # Two flat generics. Generics cannot inherit generics in Infrahub, so the - # concrete monitors multi-inherit both plus OtnGenericPort, which is the shape - # OtnAmplifierPort already uses for OtnGenericPort and OtnOpticalPort. - # - # human_friendly_id and uniqueness_constraints are not declared here. Both come - # from OtnGenericPort, and a constraint on a generic binds across every kind - # that inherits it, so (device, name) stays unique across all five monitors - # taken together. + # Monitor generics. The concrete monitors list both of these plus OtnGenericPort, + # whose (device, name) constraint then binds across all five taken together. - name: Monitor namespace: Otn description: The one fact every monitoring interface carries, whatever it measures. @@ -521,8 +486,6 @@ generics: icon: mdi:gauge include_in_menu: false attributes: - # A reading with no timestamp is not a reading. This is a last known - # value, not a telemetry feed; the age is what makes drift meaningful. - name: measured_at kind: DateTime optional: false @@ -535,14 +498,8 @@ generics: label: Channel monitor icon: mdi:chart-histogram include_in_menu: false - # A ROADM degree and a multiplexer report the same two numbers, so the two - # are declared once here rather than twice. The kinds stay distinct, - # because a degree and a multiplexer are different equipment and a query - # should be able to say which it means. It also leaves room for the two to - # diverge without a migration on loaded data. - # - # This generic does not inherit OtnMonitor. Generics do not inherit - # generics; the concrete kinds take both. + # A ROADM degree and a multiplexer report the same two numbers, so they are + # declared once here. The kinds stay distinct so a query can say which it means. attributes: - name: total_power_mdbm kind: Number @@ -562,7 +519,7 @@ generics: order_weight: 1310 nodes: - # ---- from schemas/otn_ports.yml ---- + # Ports. - name: RouterPort namespace: Otn description: Grey optics on an IP router. Not on the C-band grid. @@ -615,18 +572,9 @@ nodes: optional: true order_weight: 1900 relationships: - # The module fitted in this port, and the inverse of OtnTransceiver.port. - # Both ends are cardinality one, so the edge is one to one: the server - # refuses the second write with "has 2 peers for - # otn_optical_port__transceiver, maximum of 1 allowed". That is the - # duplicate rule, carried by the schema rather than by a check. - # - # Declared on the three concrete kinds that have a cage and never on the - # OtnOpticalPort generic, which would hand the field to all eight optical - # port kinds including the five that hold no module. - # - # Optional on both sides, so a spare, an RMA and a decommissioned unit stay - # modellable. + # The module fitted in this port, and the inverse of OtnTransceiver.port. Both + # ends are cardinality one, so a second module in one port is refused at write + # time. Declared only on the three concrete kinds that have a cage. - name: transceiver peer: OtnTransceiver label: Module fitted in this port @@ -689,8 +637,6 @@ nodes: optional: true order_weight: 1900 relationships: - # The one-to-one edge OtnRouterPort.transceiver documents, here on the kind - # that takes a grey pluggable on the transponder side. - name: transceiver peer: OtnTransceiver label: Module fitted in this port @@ -753,45 +699,10 @@ nodes: optional: true order_weight: 1900 relationships: - # The wavelength this port terminates, and the deletion boundary. - # - # Deleting a transponder already deletes this port: OtnGenericDevice.ports - # is kind: Component with on_delete: cascade, and a port has no meaning - # apart from the device it sits in. The deletion stops here. - # - # A carrier is not a component of a line port. It is a spectrum allocation - # on a route, holding a channel, a mode, its sections, its containers and - # possibly ODU switches, and it exists in the plan whether or not a box is - # currently lighting it. Cascading from this port would reach the carrier's - # containers and the services groomed into them, so pulling one transponder - # would destroy customer service records. It would also delete the - # wavelength out from under the line port at the far end, which is still - # installed: a carrier is terminated at both of its ends. - # - # Same policy as OtnService.diversity_group and OtnService.containers, for - # the same reason those two state. - # - # Light has direction, and that is not an argument for cascade here. A line - # port is a transceiver: every one of them carries both tx_power_mdbm and - # rx_sensitivity_mdbm, so there is no single transmitting end to own the - # wavelength. Cascade on a cardinality-one relationship fires from either - # port, so it would give two owners where the intuition asked for one. - # OtnOpticalCarrier holds no direction at all, deliberately: where this - # model needs direction it makes two objects, the way an amplifier hut - # does, and the budget engine evaluates a carrier both ways and takes the - # worse. Direction lives on the plant and on the path traversal, never on - # the wavelength. - # - # The model also already says "the light stopped" with status, which is - # planned, active or decommissioned. A planned carrier has no light and - # exists anyway. This kind is a spectrum allocation on a route, not the - # photons on it, and deleting a box must not silently release spectrum - # that channel_collision.py exists to police. - # - # There is a demo argument too, and it is the stronger one. A cascade makes - # the fault disappear. no-action leaves a lit carrier with nothing - # terminating it, which is visible and checkable, the shape - # monitor_completeness already works in. + # The wavelength this port terminates. The deletion boundary stops here: a + # carrier is a spectrum allocation on a route, terminated at both of its ends, + # so cascading from one port would delete the containers and the customer + # services groomed into them. - name: carrier peer: OtnOpticalCarrier label: Wavelength this port terminates @@ -801,8 +712,6 @@ nodes: identifier: otn_carrier__line_ports on_delete: no-action order_weight: 850 - # The one-to-one edge OtnRouterPort.transceiver documents, here on the kind - # that takes a coloured pluggable on the line side. - name: transceiver peer: OtnTransceiver label: Module fitted in this port @@ -970,8 +879,8 @@ nodes: order_weight: 1900 # One multiplexer channel and the common side it shares. Neither declares an - # insertion loss: the loss belongs to the device and the budget engine reads it - # from the path hop's element, so a second per-port figure would double count. + # insertion loss: that belongs to the device, so a per-port figure would double + # count. - name: MuxClientPort namespace: Otn description: One channel of a multiplexer. The side facing the transponder or router that lights it. @@ -981,12 +890,9 @@ nodes: inherit_from: - OtnGenericPort - OtnOpticalPort - # Two optional edges, not one mandatory edge to a shared channel generic. The - # dense and coarse grid kinds were kept apart, and Infrahub has no - # cross-relationship constraint, so the schema cannot say "exactly one of - # these two". mux_channel_binding owns the neither case and the both case. - # - # Neither cascades: deleting a channel must not delete the port that named it. + # A channel port should bind exactly one of these two edges. Infrahub has no + # cross-relationship constraint, so both neither and both are writable. + # Unenforced here. relationships: - name: dwdm_channel peer: OtnFrequencyGrid @@ -1007,7 +913,6 @@ nodes: on_delete: no-action order_weight: 860 - # It binds no channel because it carries every channel the device lights. - name: MuxLinePort namespace: Otn description: The common side of a multiplexer, where the whole band leaves. @@ -1028,21 +933,11 @@ nodes: - OtnGenericPort - OtnCopperPort - # The five monitors. They declare readings, because what a monitor measures is - # what distinguishes it. Eleven of the fourteen readings belong to exactly one - # family, so a single kind with a discriminator could not say which readings a - # monitor must carry and which it cannot take. Five kinds can, and the schema - # then refuses both halves: a missing reading, because the attribute is - # mandatory, and an impossible one, because the kind has no field for it. + # The five monitors. Eleven of the fourteen readings belong to exactly one family, + # so five kinds can refuse both a missing reading and an impossible one. - name: AmplifierMonitor - # Nothing here constrains which device kind a monitor may hang off, and that - # is measured rather than overlooked. Infrahub 1.11.0 refuses to let a - # concrete kind narrow the peer of a relationship it inherits, from either - # end, at schema check and before any load. OtnGenericPort.device peers - # OtnGenericDevice, so a receiver monitor on an amplifier is writable. The - # remaining option was a Python check, which is the layer these five kinds - # exist to leave, so the gap is accepted and stated instead. The generator - # that writes the monitors is the only thing that ever creates one. + # Nothing constrains which device kind a monitor hangs off: a concrete kind + # cannot narrow the peer of a relationship it inherits from OtnGenericPort. namespace: Otn description: The last reading taken from an amplifier's monitoring interface. label: Amplifier monitor @@ -1090,10 +985,8 @@ nodes: optional: true description: Output power in dBm. Read-only, derived from output_power_mdbm. order_weight: 1230 - # measured_gain_mdb is declared here and on OtnRamanMonitor, with - # identical bounds and an identical template. It cannot live on - # OtnMonitor, because that would put a gain field on the channel and - # receiver monitors, which is the failure these five kinds remove. + # Declared here and on OtnRamanMonitor rather than on OtnMonitor, which would + # put a gain field on the three monitor kinds that cannot produce one. - name: measured_gain_mdb kind: Number parameters: @@ -1121,8 +1014,7 @@ nodes: description: Gain tilt across the band in millidecibels. Negative tilts the red end down. order_weight: 1260 - # No attributes of its own. Both readings come from OtnChannelMonitor, which - # OtnMuxDemuxMonitor takes too, so the pair is declared once. + # No attributes of its own: both readings come from OtnChannelMonitor. - name: RoadmDegreeMonitor namespace: Otn description: The last reading taken from a ROADM degree's monitoring interface. @@ -1161,9 +1053,6 @@ nodes: order_by: - name__value attributes: - # The same declaration OtnAmplifierMonitor carries. Two kinds, one gain - # reading each, and no gain field on the three kinds that cannot produce - # one. - name: measured_gain_mdb kind: Number parameters: @@ -1211,9 +1100,7 @@ nodes: order_by: - name__value attributes: - # OSNR sits here and on no other monitor kind. A coherent receiver's DSP - # computes it. An amplifier reports power, not OSNR, and after the split - # there is no field on an amplifier monitor to put it in. + # OSNR sits on no other monitor kind: a coherent receiver's DSP computes it. - name: measured_osnr_mdb kind: Number parameters: @@ -1240,7 +1127,6 @@ nodes: optional: false description: Received power at the coherent front end, in millidecibel-milliwatts. order_weight: 1520 - # Parts per billion keeps a bit error rate an integer: 2.1e-3 is 2100000. - name: pre_fec_ber_ppb kind: Number parameters: @@ -1274,10 +1160,7 @@ nodes: description: Differential group delay in femtoseconds. order_weight: 1560 - # The catalog entry and the physical unit. Neither inherits anything: a part - # number is not racked and a pluggable is fitted into a port rather than being - # one. They live beside the ports they plug into, so the file count does not - # move. + # The catalog entry and the physical unit. Neither is racked, so neither inherits. - name: TransceiverType namespace: Otn description: A pluggable optic part number and what it can be made to do. @@ -1345,8 +1228,7 @@ nodes: optional: false description: The cage this part fits. It decides which ports can hold it. order_weight: 1300 - # A fixed-wavelength optic can only ever sit on the channel it was bought - # for, which is what makes this worth storing rather than deriving. + # A fixed-wavelength optic can only sit on the channel it was bought for. - name: tunable kind: Boolean default_value: false @@ -1355,7 +1237,9 @@ nodes: order_weight: 1400 relationships: # Optional, so a part number can be recorded before the mode catalog is - # populated. A type with no modes says nothing about what it can run. + # populated. Nothing compares this list against the mode a carrier actually + # runs, so a module can be fitted to a carrier it cannot support. + # Unenforced here. - name: supported_modes peer: OtnOpticalMode label: Modes this part can run @@ -1415,19 +1299,12 @@ nodes: identifier: otn_transceiver_type__units on_delete: no-action order_weight: 900 - # Optional, and therefore carrying no uniqueness constraint: Infrahub - # refuses a constraint on an optional relationship, and making this one - # mandatory would leave a spare, an RMA and a decommissioned unit - # unmodellable. - # - # The duplicate is refused anyway, by the inverse rather than by a - # constraint. OtnLinePort, OtnClientPort and OtnRouterPort each declare a - # cardinality-one `transceiver` on this identifier, so both ends are - # cardinality one and the second module in a port is refused at write time. - # - # The port kind stays with the transceiver_placement check. This peers the - # generic, and a relationship to a generic cannot be filtered by peer kind, - # so the schema accepts a module written into an amplifier port. + # Optional, so a spare, an RMA and a decommissioned unit stay modellable, and + # therefore carrying no uniqueness constraint: Infrahub refuses one on an + # optional relationship. The duplicate is refused by the inverse instead, since + # every `transceiver` on this identifier is cardinality one. This peers the + # generic, and a relationship to a generic cannot be filtered by peer kind, so + # a module written into an amplifier port is accepted. Unenforced here. - name: port peer: OtnOpticalPort label: Port this unit is fitted in @@ -1438,15 +1315,10 @@ nodes: on_delete: no-action order_weight: 950 - # ---- from schemas/otn_plant.yml ---- - # A `cardinality: one` relationship here gets an inverse when the peer is a page - # an operator lands on and the reverse list is what that page is for. Otherwise - # the reverse direction is a server-side filter on the forward side, which costs - # nothing and does not duplicate the fact. site_a and site_b have inverses on - # LocationSite, added through the `extensions:` block at the end of this file; - # roadm_a and roadm_b have inverses on OtnRoadm, defined further down in this - # same file. Every inverse reuses the identifier declared here, and an inverse - # on a new identifier loads without complaint and then reads an empty list. + # Plant. + # A `cardinality: one` relationship earns an inverse only where the peer's own page + # is what the reverse list is for; otherwise use a server-side filter on the forward + # side. An inverse must reuse the forward identifier, or it reads an empty list. - name: FiberType namespace: Otn description: Single-mode fiber family. Attenuation, dispersion and group index at 1550 nm. @@ -1474,22 +1346,8 @@ nodes: max_length: 256 optional: true order_weight: 1100 - # One coefficient per fibre family, measured at 1550 nm, and the budget - # engine applies it to every span it is given. That is right for the dense - # grid, where every channel sits between 1530 and 1565 nm. It is wrong for - # a coarse wavelength, where it understates the loss over the coarse tail - # by about a decibel, more than the margin by which a section in this - # dataset already fails. - # - # So no coarse span reaches the engine: a coarse span belongs to no - # optical multiplex section, and tests/unit/test_geant_dataset.py refuses - # an `oms` on any span touching a site of `site_type: customer`. Budgeting - # a coarse link needs a coefficient per wavelength band, which is a schema - # change. - # - # Named for the coefficient it is, because the plain `attenuation` names - # now belong to the attenuators, which hold an attenuation rather than a - # rate. + # One coefficient per fibre family, measured at 1550 nm. That is right for the + # dense grid and understates the loss on a coarse wavelength. - name: attenuation_coefficient_mdb_per_km kind: Number parameters: @@ -1603,10 +1461,8 @@ nodes: order_by: - name__value display_label: name__value - # The inherited insertion_loss_mdb stays at its default of 0 on a span. A - # span's real loss is length x attenuation plus splices plus connectors plus - # aging margin, and the budget engine computes it on demand. The stored 0 - # does not mean the span is lossless. + # The inherited insertion_loss_mdb stays at 0 on a span. A span's real loss is + # length x attenuation plus splices, connectors and aging margin. attributes: - name: name kind: Text @@ -1622,10 +1478,8 @@ nodes: max_length: 256 optional: true order_weight: 1100 - # Not unique: a uniqueness constraint cannot reference an optional - # relationship, and oms has to stay optional so a span is creatable before - # its section exists. Duplicate positions are rejected by a check, not by - # the schema. + # Not unique: a uniqueness constraint cannot reference an optional relationship, + # and oms stays optional so a span is creatable before its section exists. - name: oms_sequence kind: Number parameters: @@ -1738,13 +1592,8 @@ nodes: identifier: otn_oms__spans on_delete: no-action order_weight: 840 - # The inverse of OtnRamanPump.span, on the same identifier, and both sides - # are needed. A budget walks a section, reaches its spans and reads what - # is on them; without this side nothing on that walk can see a pump, so - # every pumped span sums to zero gain and the check passes while reporting - # a loss the network does not have. It is also what makes OtnRamanPump - # reachable in the UI, because the sidebar does not name device kinds, and - # tests/unit/test_menu.py asserts that. + # The inverse of OtnRamanPump.span, on the same identifier. A budget walks a + # section to its spans, so without this side no pump is visible on that walk. - name: raman_pumps peer: OtnRamanPump kind: Attribute @@ -1753,14 +1602,8 @@ nodes: identifier: otn_span__raman_pumps on_delete: no-action order_weight: 850 - # The ports at each end of the glass. No port kind had any edge to a span or - # a section in either direction, so the only thing naming the degree port - # facing a given span was the far site's shortname inside the port's name - # string, a naming convention doing a relationship's job. - # - # Declared on the span alone: the inverse would put the field on every port - # kind and most ports terminate no span. Empty means unjudgeable rather than - # clean, because an empty list and a fault look the same from outside. + # The ports at each end of the glass. Declared on the span alone: the inverse + # would put the field on every port kind and most ports terminate no span. - name: terminating_ports peer: OtnGenericPort label: Ports at the ends of this span @@ -1824,14 +1667,8 @@ nodes: identifier: otn_oms__spans on_delete: no-action order_weight: 900 - # One relationship per direction of travel, rather than one holding both - # chains. Which chain an amplifier is in is then stored by the graph, and - # no attribute on the amplifier can disagree with it. - # - # Both stay optional: a section is creatable before its amplifiers exist. - # A query that selects one and forgets the other is not an error at the - # server, and `peers` hands back an empty list for a key that is not - # there, so the loud failure is the budget engine's N+1 rule instead. + # One relationship per direction of travel rather than one holding both chains, + # so which chain an amplifier is in is stored by the graph. Both stay optional. - name: amplifiers_a2b peer: OtnAmplifier kind: Attribute @@ -1849,7 +1686,7 @@ nodes: on_delete: no-action order_weight: 920 - # ---- from schemas/otn_logical.yml ---- + # Logical. - name: FrequencyGrid namespace: Otn description: One ITU-T G.694.1 channel. 50 GHz fixed grid, 191.35 to 196.10 THz. @@ -1871,9 +1708,7 @@ nodes: optional: false description: ITU channel number, 1 to 96. Channel 1 is 191.35 THz. order_weight: 1000 - # Stored rather than computed so that "which channel is 193.5 THz" is a - # GraphQL range filter instead of a Python loop. A guard test recomputes - # all 96 from units.py. + # Stored rather than computed, so "which channel is 193.5 THz" is a range filter. - name: center_frequency_mhz kind: Number unique: true @@ -1893,8 +1728,7 @@ nodes: optional: true description: Centre frequency in THz. Byte-identical to the optical-port rendering. order_weight: 1510 - # The inverse of OtnOpticalCarrier.channel, on the same identifier, so a - # channel's page answers "who holds this wavelength". + # The inverse of OtnOpticalCarrier.channel, on the same identifier. relationships: - name: carriers peer: OtnOpticalCarrier @@ -1906,11 +1740,8 @@ nodes: on_delete: no-action order_weight: 900 - # The coarse plan, G.694.2, in its own kind. `OtnOpticalCarrier.channel` is - # mandatory, cardinality one, and peers `OtnFrequencyGrid`, so the server - # rejects a carrier pointed at a coarse wavelength with "must be of type: - # ['OtnFrequencyGrid']". Holding both plans in one kind would turn that - # write-time rejection into a check that runs in a pipeline. + # The coarse plan is a separate kind so that `OtnOpticalCarrier.channel`, which + # peers `OtnFrequencyGrid` alone, refuses a mispointed carrier at write time. - name: CwdmChannel namespace: Otn description: One ITU-T G.694.2 coarse wavelength. 20 nm spacing, 1271 to 1611 nm. @@ -1932,10 +1763,6 @@ nodes: optional: false description: Nominal central wavelength in nm. Wavelength n is 1271 + (n - 1) x 20. order_weight: 1000 - # Stored rather than computed, for the reason center_frequency_mhz is - # stored above: "which coarse wavelengths could an erbium amplifier reach" - # is then a GraphQL filter instead of a Python loop. A guard test - # recomputes all eighteen wavelengths and all eighteen bands from units.py. - name: band kind: Dropdown choices: @@ -1970,9 +1797,6 @@ nodes: - name__value display_label: name__value attributes: - # The regex admits a space, a plus and a slash, which the device-name - # regex does not: "OpenZR+ 400G" and "DP-16QAM 64GBd 400G" both need it. - # A human_friendly_id tolerates both characters. - name: name kind: Text unique: true @@ -2089,8 +1913,7 @@ nodes: optional: false description: Encoder and decoder latency in nanoseconds. Small next to propagation, but not zero. order_weight: 1960 - # The inverse of OtnOpticalCarrier.optical_mode, on the same identifier, so - # a mode's page answers which carriers use it. + # The inverse of OtnOpticalCarrier.optical_mode, on the same identifier. relationships: - name: carriers peer: OtnOpticalCarrier @@ -2144,27 +1967,18 @@ nodes: - name: fibre_channel label: Fibre Channel color: "#ff9800" - # Being declared here says nothing about automatic selection. That - # is `auto_selectable` below, and it is per signal, not per layer. - name: infiniband label: InfiniBand color: "#673ab7" optional: false description: Grouping the catalog by layer is the first thing a reader does. order_weight: 1200 - # Whether the rate rule may pick this signal when a service states none. - # - # Per signal, where the Python allow-list it replaces was per layer. - # Finer, and strictly more expressive: two rows on one layer may differ. - # The measured reason every InfiniBand row is false is recorded in - # `objects/04_client_signals.yml`. + # Per signal, where the rule it replaces was per layer: two rows on one layer + # may differ. - name: auto_selectable kind: Boolean - # `default_value: false` with `optional: false` fails closed: a row added - # without a decision is unreachable by the automatic path until somebody - # writes `true` in a diff. A default of true would fail open, and the - # next specialised signal would be handed to a service that never asked - # for it, silently. That is the defect this flag exists to prevent. + # Fails closed: a row added without a decision is unreachable by the + # automatic path until somebody writes true. default_value: false optional: false description: May the rate rule pick this signal when a service states none. @@ -2175,8 +1989,6 @@ nodes: max_length: 256 optional: true order_weight: 1300 - # kbps, not Mbps, for the same reason OtnCopperPort.speed_kbps is: E1 is - # 2.048 Mbps and rounds to 2 as an integer. - name: bit_rate_kbps kind: Number parameters: @@ -2185,9 +1997,6 @@ nodes: optional: false description: Nominal line rate in kbps. E1 is 2048, 100GBASE-LR4 is 103100000. order_weight: 1500 - # Two divisors in one template. The catalog spans seven orders of - # magnitude, so a single divisor renders E1 as 0.002048 Gbps or - # 400GBASE-FR4 as 412500.0 Mbps. - name: bit_rate_display kind: Text read_only: true @@ -2238,8 +2047,7 @@ nodes: identifier: otn_client_signal__containers on_delete: no-action order_weight: 900 - # The inverse of OtnService.client_signal, so a catalog row's page answers - # "which services hand this over". + # The inverse of OtnService.client_signal, on the same identifier. - name: services peer: OtnService kind: Attribute @@ -2269,12 +2077,8 @@ nodes: regex: "^[A-Za-z0-9._-]+$" optional: false order_weight: 1000 - # Sixteen values: ClientSignal.default_container_type's fourteen, plus - # ODUC6 and ODUC8. Those two are line containers for the 600G and 800G - # modes and no client maps directly into one, so they belong here and not - # in the client enum. - # VC-12, VC-4 and STM-N are here because an E1 maps into VC-12 into STM-N - # into ODU1, and without them that chain cannot be written at all. + # OtnClientSignal.default_container_type's fourteen values plus ODUC6 and + # ODUC8, which are line containers no client maps directly into. - name: odu_type kind: Text enum: @@ -2311,14 +2115,9 @@ nodes: max_length: 256 optional: true order_weight: 1300 - # Which segment of its circuit this container rides. The same field and - # the same default as OtnOpticalPath.segment_sequence, and read the same - # way: a container written before this feature rode the one and only - # segment, so 1 is the correct value and not just a value that loads. - # - # A circuit is therefore its containers ordered by this, and its - # wavelengths are its paths ordered by this. Two readings of one sequence - # from two places, and a test asserts they agree. + # Which segment of its circuit this container rides. A circuit is its + # containers ordered by this, as its wavelengths are its paths ordered by + # OtnOpticalPath.segment_sequence. - name: segment_sequence kind: Number default_value: 1 @@ -2328,16 +2127,6 @@ nodes: optional: false description: Which segment of its circuit this container rides. 1 for a circuit that spans one wavelength. order_weight: 1400 - # Two numbers, not one. tributary_slots is what this container occupies in - # its parent; tributary_slot_capacity is what it offers to its children. - # The capacity check sums the first across children and compares it to - # the second on the parent, which needs both to exist. - # - # 640 on both bounds is ODUC8, the widest row in containers.SLOT_TABLE and - # the line container for the fastest mode in the catalog. A bound below the - # widest row rejects a container the slot arithmetic considers legal, so - # `tests/unit/test_containers.py` recomputes 640 from the table and fails - # if either number moves alone. - name: tributary_slots kind: Number default_value: 0 @@ -2356,14 +2145,11 @@ nodes: optional: false description: Slots this container offers to its children. An ODU4 offers 80. order_weight: 1600 - # The two `direction` keys are mandatory. Both sides default to - # bidirectional, which collides on a shared identifier and is rejected at - # load time. outbound on the parent side and inbound on the child side - # resolves to one edge: write only the child's parent_container and the - # parent reports the child with no second write. + # The two `direction` keys are mandatory. Both sides default to bidirectional, + # which collides on a shared identifier and is rejected at load time. relationships: - # The wavelength this container rides. Optional, because a container may - # be multiplexed into a parent that holds the carrier instead. + # Optional, because a container may be multiplexed into a parent that holds the + # carrier. - name: carrier peer: OtnOpticalCarrier kind: Attribute @@ -2389,22 +2175,12 @@ nodes: direction: outbound on_delete: no-action order_weight: 810 - # Which circuit owns this container. Only a client container carries one. A - # line container leaves it empty on purpose: it belongs to a wavelength, - # and naming a service on it would claim that one of the several services - # groomed into that wavelength owns the whole of it. + # Only a client container carries one. A line container leaves it empty on + # purpose: it belongs to a wavelength that several services may share. - name: service - # Stored, not derived, and it cannot be derived. The chain container to - # parent_container to carrier to optical_path to service does exist, but - # `OtnOpticalCarrier.optical_path` is cardinality many since grooming - # let two services share a wavelength, so that walk yields every service - # on the wavelength rather than the one that owns this container. That - # is the right answer for `transforms/impact_report.py`, whose question - # is who is affected by a cut, and the wrong one for - # `transforms/service_trace.py`, which listed a neighbour's containers - # as part of a service's own circuit with nothing raised. The naming - # convention `odu-` was the only link before this, and nothing - # read it. + # Stored, not derived. OtnOpticalCarrier.optical_path is cardinality many + # since grooming, so walking back through the carrier yields every service on + # the wavelength rather than the one that owns this container. peer: OtnService kind: Attribute cardinality: one @@ -2422,7 +2198,7 @@ nodes: on_delete: no-action order_weight: 900 - # ---- from schemas/otn_devices.yml ---- + # Devices. - name: Router namespace: Otn description: IP router. Light terminates here, so it contributes no insertion loss. @@ -2451,9 +2227,8 @@ nodes: inherit_from: - OtnGenericDevice - OtnOpticalElement - # The inverses of OtnOpticalMultiplexSection.roadm_a and .roadm_b, each on - # the identifier its forward side declares. A ROADM inherits site at 800 and - # ports at 900, so these follow at 960 and 970. + # The inverses of OtnOpticalMultiplexSection.roadm_a and .roadm_b, each on the + # identifier its forward side declares. relationships: - name: sections_a peer: OtnOpticalMultiplexSection @@ -2483,14 +2258,10 @@ nodes: inherit_from: - OtnGenericDevice - OtnOpticalElement - # oms_sequence is stored because Infrahub relationships carry no order, so - # OtnOpticalMultiplexSection.amplifiers hands back a set. The budget cannot - # be walked over a set: the loss ahead of an amplifier is the loss of - # whatever sits immediately before it. OtnFiberSpan carries an explicit - # position for the same reason. + # oms_sequence is stored because Infrahub relationships carry no order, so a + # section hands back a set and a budget cannot be walked over one. attributes: - # 3000 is the quantum-limited floor for an EDFA and 10000 is a bad one, so - # the bounds bracket the whole plausible range rather than this dataset's. + # 3000 is the quantum-limited floor for an EDFA and 10000 is a bad one. - name: noise_figure_mdb kind: Number default_value: 4000 @@ -2529,16 +2300,9 @@ nodes: optional: true description: Gain in dB. Read-only, derived from gain_mdb. order_weight: 1550 - # No direction attribute. An amplifier hut is bidirectional and this model - # gives each direction its own object, but which chain an object is in is - # the relationship holding it, oms_a2b or oms_b2a, and an attribute - # restating that would be a second copy that can disagree with the first. - # - # It does two jobs and both are its own. It orders a chain that the - # relationship has already identified, so a sorted chain is in traversal - # order and the engine never reverses one. And it fixes which member of - # that chain is which: position 1 is the booster, position N+1 is the - # pre-amplifier, and amplifier k feeds span k of its own walk. + # No direction attribute: which chain an amplifier is in is the relationship + # holding it, oms_a2b or oms_b2a. This orders that chain and fixes which member + # is which: position 1 is the booster and position N+1 the pre-amplifier. - name: oms_sequence kind: Number parameters: @@ -2546,36 +2310,19 @@ nodes: # 51, not 50: a section with N spans carries N+1 amplifiers per # direction, and the span attribute caps N at 50. max_value: 51 - # Mandatory, and with no default_value on purpose. A default of 1 would - # let a new amplifier take a silent position at the head of its chain, - # collide with the real first amplifier, and sort stably into a wrong - # answer rather than raising. - # - # Not unique, and it cannot be: a uniqueness constraint cannot reference - # an optional relationship, and the section relationships have to stay - # optional so an amplifier is creatable before its section exists. - # Rejecting duplicate positions is a test's job. + # No default_value on purpose: a default of 1 would let a new amplifier take + # a silent position at the head of its chain and sort into a wrong answer. optional: false description: Position in this amplifier's own chain, counting along the direction it amplifies. order_weight: 1560 # The two inverses of the section's two amplifier relationships, each on the - # matching identifier. Exactly one is set on any amplifier, and which one it - # is answers the direction question the deleted attribute used to answer. - # - # Two, not one, because one relationship cannot be the inverse of two - # identifiers. The alternative was dropping the amplifier's section - # relationship entirely and reaching the section by server-side filter, and - # that was rejected: an amplifier page is a page an operator lands on, and - # "which section and which way" is what it is for. The honest accounting is - # that this trades one attribute for one relationship and the schema does - # not get shorter. + # matching identifier. Exactly one is set, and which one answers the direction. relationships: - name: oms_a2b peer: OtnOpticalMultiplexSection kind: Attribute cardinality: one - # Both optional, because an amplifier is creatable before its section - # exists. + # Both optional, because an amplifier is creatable before its section exists. optional: true identifier: otn_oms__amplifiers_a2b on_delete: no-action @@ -2600,9 +2347,7 @@ nodes: - OtnOpticalElement relationships: # The wavelengths a coarse multiplexer lights are a property of the device - # rather than of the plan. Without this relationship OtnCwdmChannel is a - # kind nothing points at, which fails the sidebar reachability test in - # tests/unit/test_menu.py. + # rather than of the plan. - name: cwdm_channels peer: OtnCwdmChannel kind: Attribute @@ -2628,24 +2373,15 @@ nodes: label: Raman pump icon: mdi:laser-pointer include_in_menu: false - # Two generics, listed side by side. Infrahub's GenericSchemaWrite has no - # inherit_from key, so OtnOpticalElement cannot inherit OtnGenericDevice and - # a kind that is both a racked device and something light passes through has - # to say so twice. - # - # No attribute storing which direction the pump amplifies. That is the - # conclusion, and injection_end and propagation are the two physical facts - # that compute it: a counter-propagating pump fires back up the fibre from - # the far end, so one at the B end amplifies the A to B signal, and a - # co-propagating pump at the A end amplifies it too. + # Two generics side by side: a kind that is both a racked device and something + # light passes through has to say so twice. No attribute stores which direction + # the pump amplifies; injection_end and propagation are what compute it. inherit_from: - OtnGenericDevice - OtnOpticalElement attributes: - # On-off gain: the difference in received signal power with the pump on - # against the pump off. It reduces the effective loss of the span it sits - # on rather than adding a stage to the chain, which is why the budget - # subtracts it from the fiber loss and no amplifier object is involved. + # On-off gain is received signal power with the pump on against the pump off. + # It reduces the effective loss of its span rather than adding a stage. - name: on_off_gain_mdb kind: Number default_value: 10000 @@ -2665,15 +2401,6 @@ nodes: optional: true description: On-off gain in dB. Read-only, derived from on_off_gain_mdb. order_weight: 1530 - # Where the pump is spliced in. The two choices name the span's own - # endpoint relationships, site_a and site_b, rather than inventing a - # vocabulary. - # - # default_value exists because a mandatory attribute needs one for the - # schema to load, and site_b is what every shipped counter-propagating - # forward pump takes. It is a loading device and not an assertion: the - # generator writes the value explicitly on every pump, and injection_end - # is in NEVER_SUPPRESSED so the generated file always shows it. - name: injection_end kind: Dropdown default_value: site_b @@ -2687,11 +2414,9 @@ nodes: optional: false description: Which end of its span the pump is injected at, named against the span's own site_a and site_b. order_weight: 1540 - # Counter-propagating is the default: the pump fires against the signal, - # so pump noise is averaged over the span instead of landing on the signal - # at its weakest point. The budget reads this and injection_end together: - # the direction a pump amplifies is - # (injection_end == site_a) == (propagation == co). + # Counter-propagating is the default: the pump fires against the signal, so pump + # noise is averaged over the span instead of landing on it at its weakest point. + # The direction a pump amplifies is (injection_end == site_a) == (propagation == co). - name: propagation kind: Dropdown default_value: counter @@ -2705,9 +2430,7 @@ nodes: optional: false description: Whether the pump fires against the signal or with it. order_weight: 1550 - # Mandatory: a detached pump is gain the budget can never find. The inverse - # `raman_pumps` lives on OtnFiberSpan in otn_plant.yml and carries the same - # identifier. + # Mandatory: a detached pump is gain no budget can find. relationships: - name: span peer: OtnFiberSpan @@ -2724,25 +2447,16 @@ nodes: label: ODU switch icon: mdi:swap-horizontal-variant include_in_menu: false - # Two flat generics side by side, the composition OtnMuxDemux already uses. - # OtnOpticalElement is here because OtnPathHop.element peers that generic, - # and a segment's route has to be able to name the device it terminates on. - # - # insertion_loss_mdb arrives with OtnOpticalElement and applies to the - # incoming segment only. This device terminates the light rather than - # passing it through, so the outgoing segment starts at a transmitter and - # not at an attenuated signal. That asymmetry is why the loss cannot be - # added into one total spanning both segments: each segment carries its own - # budget and the device is the boundary between them. + # OtnOpticalElement is here because OtnPathHop.element peers that generic. The + # inherited insertion_loss_mdb applies to the incoming segment only: this device + # terminates the light, so the outgoing segment starts at a transmitter. inherit_from: - OtnGenericDevice - OtnOpticalElement attributes: - # `regenerator` is the default because it is the cheaper device and the - # one a route that does not close needs: it carries the whole payload - # across without looking inside it. `cross_connect` demultiplexes to - # containers and regroups them, which is what lets the segments either - # side carry different clients. + # `regenerator` is the default: it carries the whole payload across without + # looking inside it. `cross_connect` demultiplexes to containers and regroups + # them. - name: switching_mode kind: Dropdown default_value: regenerator @@ -2756,47 +2470,21 @@ nodes: optional: false description: Whether the device carries the whole payload through or demultiplexes and regroups containers. order_weight: 1300 - # Nanoseconds as an integer, the unit OtnOpticalMode.fec_latency_ns and - # OtnOpticalPath.latency_ns already use. No Float kind exists. - # - # No paired _display, for the reason fec_latency_ns has none: the whole - # plausible range is hundreds of nanoseconds, and the repository's only - # nanosecond rendering divides by 1000, so every device would read - # "0.0 us". The figure an operator wants is the circuit total, and - # latency_display on the path renders that. + # Nanoseconds, the unit fec_latency_ns and latency_ns already use. No paired + # _display: the whole plausible range is hundreds of nanoseconds. - name: framing_latency_ns kind: Number - # A device nobody has characterised adds nothing to the circuit rather - # than adding a guess. default_value: 0 parameters: min_value: 0 - # fec_latency_ns's ceiling, reused. A framing delay of 100 us would be - # the largest single term in the whole latency budget. max_value: 100000 optional: false description: Delay this device adds in nanoseconds, from framing and from the electrical crossing. order_weight: 1520 - # The wavelengths this device terminates. It is what - # src/infrahub_demo_otn/chains.py reads to learn which carriers a junction - # can join, and it is the whole of the junction predicate: a device with an - # empty list contributes no junction. relationships: - # It is not what finds the chain, and R-008 measured that rather than - # assuming it. A device-to-device traversal filtered on this edge alone - # returns zero paths, because a ROADM has no edge to a carrier and a - # carrier has no edge to a device other than this one. Widen the filter - # until paths come back and dropping this edge from it changes nothing - # about which paths come back: 100 either way, and 48 of those join two - # carriers at the one section all 71 of them cross, with nothing there to - # terminate the light and re-originate it. Choosing the cover is a search - # over the carriers this relationship names, not a traversal this - # relationship makes possible. - # - # Attachment, not termination: `oxc-mil-01` is patched to 37 wavelengths - # that terminate on Milan transponders. `OtnLinePort.carrier` is the - # termination answer. The identifier stays put; Infrahub freezes it at - # first load and rejects a change with `not_supported`. + # Attachment, not termination: a cross-connect is patched to wavelengths that + # terminate on transponders elsewhere. OtnLinePort.carrier is the termination + # answer. - name: carriers peer: OtnOpticalCarrier label: Carriers patched to this shelf @@ -2804,24 +2492,12 @@ nodes: cardinality: many optional: true identifier: otn_odu_switch__carriers - # Matching the other side and every other cross-reference here. A device - # and a wavelength are independent objects: deleting either one leaves - # the other. on_delete: no-action order_weight: 1900 - # Two attenuator kinds rather than one carrying an attenuator_type Dropdown and - # an optional range. That is the split the five monitor kinds already argue - # for: a pad with a maximum is a field that should not exist, and with two - # kinds it does not. - # - # Neither kind declares ports. Both inherit `ports` from OtnGenericDevice and - # hold none, and both reach a path through OtnPathHop.element the way - # OtnFiberSpan does. - # - # The budget engine adds attenuation_mdb to the inherited insertion_loss_mdb - # itself. No summed total is stored: one number could not say which half is the - # device and which is the setting. + # Two attenuator kinds rather than one carrying a type Dropdown and an optional + # range: a pad with a maximum is a field that should not exist. Neither declares + # ports; both inherit `ports` from OtnGenericDevice and hold none. - name: FixedAttenuator namespace: Otn description: A pad. One fixed amount of loss, patched into a link that arrives too hot. @@ -2881,10 +2557,9 @@ nodes: optional: true description: Attenuation in dB. Read-only, derived from attenuation_mdb. order_weight: 1910 - # The absolute ceiling is on the attribute, so the schema refuses a - # physically impossible figure at write time. It cannot refuse a setting - # past this device's own maximum, a sibling attribute's value, so - # attenuator_range owns that half and only that half. + # The absolute ceiling is on the attribute, so the schema refuses a physically + # impossible figure at write time. It cannot refuse a setting past this device's + # own maximum, which is a sibling attribute's value. Unenforced here. - name: max_attenuation_mdb kind: Number parameters: @@ -2904,7 +2579,7 @@ nodes: description: Maximum attenuation in dB. Read-only, derived from max_attenuation_mdb. order_weight: 1930 - # ---- from schemas/otn_carrier.yml ---- + # Carrier. - name: OpticalCarrier namespace: Otn description: One provisioned wavelength. Occupies its channel on every section it crosses. @@ -2931,8 +2606,7 @@ nodes: max_length: 256 optional: true order_weight: 1100 - # A carrier the generator writes on a branch stays `planned` until the - # change merges; a pre-provisioned one is `active`. + # A carrier written on a branch stays `planned` until the change merges. - name: status kind: Dropdown default_value: active @@ -2965,35 +2639,20 @@ nodes: identifier: otn_carrier__optical_mode on_delete: no-action order_weight: 810 - # Many, not one, and the change is the grooming model showing through. A - # wavelength used to carry one service, so one optical path per carrier was - # the truth. Once two services groom their client containers into the same - # line container, both of their paths ride this carrier. Each service still - # has exactly one path of its own, which is `otn_service__optical_path` and - # is untouched. - # - # The name stays singular although the cardinality is now many, a - # deliberate ugliness rather than an oversight. See the identifier below. + # Many, not one, because two services grooming into one line container both + # ride this carrier. Each service still has exactly one path of its own. The + # name stays singular although the cardinality is many; see the identifier below. - name: optical_path peer: OtnOpticalPath kind: Attribute - # Measured rather than assumed: with cardinality one, the second service - # to groom into a shared wavelength failed provisioning outright with - # "Node has 2 peers for otn_carrier__optical_path, maximum of 1 - # allowed". That is the schema refusing the thing the feature exists to - # do. cardinality: many optional: true - # Renaming to `optical_paths` is a relationship rename, and Infrahub - # reads the old and the new as two relationships on one identifier: - # "Identifier of relationships must be unique for a given direction". - # Widening a cardinality migrates cleanly, renaming does not, and the - # rename would cost a re-bootstrap of every branch to buy a plural. + # Not renamed to `optical_paths`: Infrahub reads the old and the new as two + # relationships on one identifier and refuses the load. identifier: otn_carrier__optical_path on_delete: no-action order_weight: 820 - # Unordered, and it does not need to be: occupancy is a set membership - # question. Ordering along a route lives on the path hops. + # Unordered: occupancy is set membership. Ordering along a route is on the hops. - name: sections peer: OtnOpticalMultiplexSection kind: Attribute @@ -3002,8 +2661,7 @@ nodes: identifier: otn_carrier__sections on_delete: no-action order_weight: 900 - # What the wavelength carries, so "what dies if this carrier dies" is a - # read of the carrier rather than a reverse filter. + # What the wavelength carries, so "what dies with this carrier" is a read of it. - name: containers peer: OtnContainer kind: Attribute @@ -3012,16 +2670,8 @@ nodes: identifier: otn_carrier__containers on_delete: no-action order_weight: 910 - # The inverse of OtnOduSwitch.carriers, and both sides write - # `otn_odu_switch__carriers` by hand. Leaving it off would have Infrahub - # derive one per side from that side's own kind and peer, which agrees - # only when the two pairs mirror each other, and the derived string is - # frozen the moment a load succeeds. Changing it afterwards is rejected - # with `not_supported` and costs a remove-and-re-add on both sides. - # - # Attachment, not termination. A regenerator terminates the two - # wavelengths it joins; a cross-connect grooms behind a transponder and - # terminates nothing. `line_ports` is the termination answer. + # The inverse of OtnOduSwitch.carriers, both sides writing the identifier by + # hand. Attachment, not termination: `line_ports` is the termination answer. - name: odu_switches peer: OtnOduSwitch label: ODU switches this carrier is patched to @@ -3031,22 +2681,9 @@ nodes: identifier: otn_odu_switch__carriers on_delete: no-action order_weight: 920 - # The inverse of OtnLinePort.carrier, both sides writing - # otn_carrier__line_ports by hand, on the same argument odu_switches above - # makes: a derived identifier is frozen the moment a load succeeds and - # changing it afterwards costs a remove-and-re-add on both sides. - # - # Many, because a wavelength is terminated at each of its ends, and those - # are two ports on two devices at two sites. - # - # on_delete: no-action, and here the case is plainer than on the port side: - # cascade would mean deleting a wavelength deletes the physical ports that - # terminated it, so retiring a service would remove hardware from the - # inventory. - # - # Optional, and it is genuinely reachable: generators/optical_service.py - # provisions a wavelength and binds no line port, so a freshly provisioned - # carrier has an empty list here. + # The inverse of OtnLinePort.carrier, both sides writing the identifier by + # hand. Many, because a wavelength is terminated at each of its two ends. + # no-action: deleting a wavelength must not delete the ports that terminated it. - name: line_ports peer: OtnLinePort label: Line ports terminating this wavelength @@ -3057,15 +2694,13 @@ nodes: on_delete: no-action order_weight: 930 - # ---- from schemas/otn_service.yml ---- + # Service. - name: Service namespace: Otn description: Customer intent. Two endpoints, a rate, a profile and an optional latency budget. label: Service icon: mdi:file-document-outline include_in_menu: false - # `CoreArtifactTarget` because a service is the target of a generator - # definition. inherit_from: - CoreArtifactTarget human_friendly_id: @@ -3095,9 +2730,8 @@ nodes: optional: false description: Who bought it. A Text field, because this model has no organisation kind. order_weight: 1200 - # Customer intent, not a modulation format. The generator enumerates every - # mode whose line rate meets this number; the mode it picks lands on the - # carrier. + # Customer intent, not a modulation format. The mode chosen to meet it lands on + # the carrier. - name: rate_gbps kind: Number parameters: @@ -3124,9 +2758,8 @@ nodes: color: "#607d8b" optional: false order_weight: 1400 - # `rejected` is what the generator writes when it refuses a service. - # Without it a refusal is indistinguishable from a generator that never - # ran. + # `rejected` is what a refusal writes; without it a refusal looks like nothing + # ran at all. - name: status kind: Dropdown default_value: planned @@ -3148,8 +2781,7 @@ nodes: color: "#9e9e9e" optional: false order_weight: 1500 - # The first three profiles carry a latency budget, the last two leave it - # null, and the impact report filters on this. + # The first three profiles carry a latency budget, the last two leave it null. - name: service_profile kind: Dropdown default_value: ip-transit @@ -3189,28 +2821,9 @@ nodes: optional: true description: Latency budget in microseconds. Read-only, derived from max_latency_ns. order_weight: 1710 - # The refusal, split in two. It was one Text attribute holding - # `"{code}: {detail}"`, and the code half was a Python constant that no - # schema knew about, so a typo in it became a seventh reason nobody read - # and no write refused it. - # - # Six choices, and the colours carry information the old string could not. - # Grey means the request was unanswerable: no route between the endpoints, - # or no transponder mode that meets the rate. Red means the physics - # refused a route that exists: the optical budget or the latency budget. - # Amber means the network is full: no spectrum on the corridor, or no - # tributary slots left in the containers. An operator reading a list of - # refusals learns from the colour alone whether to re-plan, to regenerate - # or to build. - # - # Optional, because a provisioned service carries no refusal. The pairing - # of a `rejected` status with an empty code is not a state the generator - # can reach, and `checks/provisionable.py` fails closed on it rather than - # reporting green for a refusal nobody can read. - # - # `tests/unit/test_schema_contract.py` asserts these six names equal the - # six Python constants. A code added to one and not the other would - # otherwise fail at write time on a live branch and nowhere earlier. + # A coded refusal rather than a free string, so a typo cannot become a seventh + # reason nobody reads. The colours group them: grey is unanswerable, red is the + # physics refusing a route that exists, amber is the network full. - name: rejection_code kind: Dropdown choices: @@ -3241,9 +2854,8 @@ nodes: optional: true description: Why the service was refused, as one of six codes. Empty whenever a path exists. order_weight: 1801 - # The prose half of the old string. 512 is what `rejection_reason` - # carried and what the generator already truncates to, so the cap is - # unchanged rather than newly imposed. + # The prose half of the refusal, never parsed. 512 is what the old single + # string carried. - name: rejection_detail kind: Text parameters: @@ -3251,22 +2863,8 @@ nodes: optional: true description: What the refusal looked like in detail. The prose beside the code, never parsed. order_weight: 1802 - # Only a person ever sets this to true. It is a signature on a refusal: - # somebody read the code and the detail, decided the demo wants the - # refusal recorded rather than fixed, and let the branch merge. - # - # `optional: false` with `default_value: false` is what makes it safe to - # add to a kind that already has rows. Mandatory with no default fails - # validation against every existing service and blocks the whole schema - # update; the default lands `false` on all of them, which is the correct - # reading of every service written before this feature. - # - # The generator may only clear it, and only on the path where it - # provisions the service, because the refusal that was signed for has - # ceased to exist. While the service remains refused the generator leaves - # it alone. Both halves matter and the natural mistake is to treat it as a - # third field beside the code and the detail and clear it on every rerun, - # which silently un-accepts a decision somebody signed. + # `optional: false` with `default_value: false` is what makes this safe to add + # to a kind that already has rows, and false is the right reading of each. - name: refusal_accepted kind: Boolean default_value: false @@ -3275,8 +2873,7 @@ nodes: order_weight: 1803 relationships: # Peered at the generic, not at OtnRouter, so a service can terminate on a - # transponder. Both endpoints are one-sided: "which services land on this - # device" is a native filter on the service side. + # transponder. One-sided: "which services land here" is a filter on this side. - name: endpoint_a peer: OtnGenericDevice kind: Attribute @@ -3293,36 +2890,18 @@ nodes: identifier: otn_service__endpoint_z on_delete: no-action order_weight: 810 - # Many, not one, because a circuit regenerated at an intermediate site is - # one path per wavelength and each path carries its own budget. The order - # is `segment_sequence` on the path, since an Infrahub relationship hands - # back a set. - # - # The widening is not free. The GraphQL selection shape changes from - # `optical_path { node { ... } }` to `optical_path { edges { node ... } }`, - # and every query already selecting it is broken from the moment this - # loads until it is migrated. Three were: service_trace.gql, - # service_latency.gql and srlg_exposure.gql. span_impact.gql was NOT, - # which was measured rather than assumed: it reached optical_path from - # OtnOpticalCarrier, and feature 016 had already widened that one. + # Many, not one, because a circuit regenerated at an intermediate site is one + # path per wavelength, each with its own budget. Order by segment_sequence. - name: optical_path peer: OtnOpticalPath kind: Attribute - # `schema check` cannot say any of this, because it does not execute a - # `.gql`. Feature 016 measured the failure this warns about: an - # unmigrated query fails at repository sync naming `NestedEdged`, - # a type in no schema file and no documentation. cardinality: many optional: true identifier: otn_service__optical_path on_delete: no-action order_weight: 900 - # Optional: a service that names no client signal is still provisionable - # from its rate alone. - # - # `queries/optical_service.gql` has to select it. A relationship the query - # does not ask for is a relationship the generator cannot see, and the - # failure is silent. + # Optional: a service naming no client signal is still provisionable from its + # rate alone. - name: client_signal peer: OtnClientSignal kind: Attribute @@ -3331,48 +2910,21 @@ nodes: identifier: otn_service__client_signal on_delete: no-action order_weight: 910 - # Two services pointing at the same group are declaring that their routes - # must not share a conduit. - # - # A relationship, and this node carried a Text attribute for it first, which - # is why the point is worth keeping. Two services were in one group only - # when their strings matched exactly, so `gold-pair` and `gold_pair` were - # two groups of one and the check passed both in silence. Silence is also - # what an absent group means, so nothing distinguished "no requirement - # declared" from "requirement declared and mistyped", and the second is the - # one that costs a customer a circuit. A regex narrows the ways to typo it - # and removes none. Feature 016 shipped the same mistake in different - # clothes, encoding a container's owning service in the container's name. - # - # An identifier either resolves to the group object or it fails at write - # time, so there is no near-miss for the check to pass in silence. - # - # Optional, and that is the whole of the design. A service with no group - # is not checked at all, so an exposure an operator accepted deliberately - # stays accepted and the check says nothing about it. The check flags a - # violated declaration, never a shared conduit on its own, which is what - # answers the objection `transforms/srlg_exposure.py` records about - # blocking a merge on a decision somebody already made. + # Two services pointing at one group declare that their routes must not share a + # conduit. A relationship, not a string: an identifier either resolves or the + # write fails. Optional, so an exposure accepted deliberately stays accepted. - name: diversity_group peer: OtnDiversityGroup kind: Attribute cardinality: one optional: true identifier: otn_diversity_group__services - # Matching every other cross-reference here. A group and a service are - # independent objects: deleting the group ends the requirement and must - # not delete the circuits that carried it. + # A group and a service are independent: deleting the group ends the + # requirement. on_delete: no-action order_weight: 915 - # The client containers this circuit owns. Written from the container side, - # which is where the comment on `OtnContainer.service` explains why the - # ownership is stored rather than walked back through the carrier: a carrier - # holds many optical paths now, so the walk answers with every service on - # the wavelength. - # - # `on_delete: no-action` matches every other container relationship. A - # deleted service leaves its container behind, inert, the same way a - # reclaimed carrier leaves its line container behind. + # The client containers this circuit owns, written from the container side. A + # deleted service leaves its containers behind, inert. - name: containers peer: OtnContainer kind: Attribute @@ -3382,24 +2934,12 @@ nodes: on_delete: no-action order_weight: 920 - # A node, not a Text attribute and not a Dropdown, and the two rejections have - # different reasons. - # - # Not a Text attribute, because that is a naming convention encoding a link in - # a string, and the comment on the retired `OtnService.diversity_group` - # attribute above has the failure it produces. - # - # Small on purpose. Everything about which routes are actually disjoint is - # computed from the spans, so this kind holds the declaration and nothing else. + # A node, not a Text attribute and not a Dropdown. Small on purpose: everything + # about which routes are actually disjoint is computed from the spans. - name: DiversityGroup namespace: Otn - # Not a `Dropdown`, because a Dropdown's choices are schema. Adding a group - # would then be a schema migration, run against loaded data, when what an - # operator is doing is writing down a promise made to a customer that - # morning. A node makes that an object write. A Dropdown also has nowhere to - # put the rest of what a group is: what it is for, who asked for it, what - # level of diversity was sold. `description` carries that, and a `Dropdown` - # choice has only a label and a colour. + # Not a `Dropdown`: a Dropdown's choices are schema, so adding a group would be a + # migration, and a choice has nowhere to put what the group is for. description: A declared diversity requirement. The services in one group must route over disjoint conduits. label: Diversity group icon: mdi:call-split @@ -3410,14 +2950,8 @@ nodes: - name__value display_label: name__value attributes: - # Unique, because the name is the identity an operator types and the - # `human_friendly_id` above resolves. Two groups of one name would make - # the reference ambiguous, which is the failure the Text attribute had. - # - # The same length and regex as every other name in this schema. Here the - # regex is cosmetic rather than load-bearing: a typo now names a group - # that does not exist and the write fails, instead of silently naming a - # new group of one. + # Unique: the name is the identity an operator types and the human_friendly_id + # resolves. - name: name kind: Text unique: true @@ -3426,8 +2960,7 @@ nodes: regex: "^[A-Za-z0-9._-]+$" optional: false order_weight: 1000 - # Why the group exists, in the operator's words. This is the field a - # string could not have carried, and it is half of why the group is a node. + # Why the group exists, in the operator's words: half of why this is a node. - name: description kind: Text parameters: @@ -3436,16 +2969,8 @@ nodes: order_weight: 1100 relationships: # The inverse side, sharing one explicit identifier with - # `OtnService.diversity_group`. Both sides carry it by hand: Infrahub keys - # a relationship by its identifier rather than by the two peer kinds, the - # auto-derived form is only safe when both sides' kind-and-peer pairs - # mirror each other, and it is frozen once loaded. Two diverging - # identifiers do not error, they split into two one-way edges that read - # empty from the other side. - # - # The check reads the group's services from here, in one selection, which - # is the other half of why this is a node: the Text attribute forced a - # scan of every service to reassemble a group by string equality. + # `OtnService.diversity_group`. Two diverging identifiers do not error, they + # split into two one-way edges that read empty from the other side. - name: services peer: OtnService kind: Attribute @@ -3466,22 +2991,11 @@ nodes: order_by: - name__value display_label: name__value - # No two segments of one circuit carry the same sequence number. The server - # refuses the second write; nothing has to notice it afterwards. - # - # The shape is a relationship plus an attribute of this node, the normal - # case, which `OtnGenericPort` already uses as `["device", "name__value"]`. - # What it could not be is `["optical_path", "segment_sequence__value"]` on - # `OtnService`: the attribute half has to belong to the node the constraint - # sits on, and `segment_sequence` belongs to the path. - # - # It needs `service` to be mandatory, which it now is. The measurement that - # allowed that is on the `service` relationship below. + # No two segments of one circuit carry the same sequence number; the server + # refuses the second write. The attribute half has to belong to the node the + # constraint sits on, which is why it cannot be declared on `OtnService`. uniqueness_constraints: - # It does not cover a gap. It refuses a repeat of segment 2 and is blind - # to the sequence 1, 2, 4, because a schema constrains what is written and - # cannot notice what is missing. The completeness check owns that ground, - # and why is in `docs/docs/client-mapping.mdx`. + # It refuses a repeat of segment 2 and is blind to the sequence 1, 2, 4. - ["service", "segment_sequence__value"] attributes: - name: name @@ -3498,33 +3012,18 @@ nodes: max_length: 256 optional: true order_weight: 1100 - # Which segment of its circuit this path is. Mandatory, and unlike - # OtnAmplifier.oms_sequence it carries a default, because the kinds this - # feature touches all have live instances and a mandatory attribute with - # no default fails validation against every one of them and blocks the - # whole schema update. Not unique on its own: segment 1 exists once per - # circuit, not once in the network, and the pair with the service is the - # `uniqueness_constraints` entry above. + # Which segment of its circuit this path is. Not unique on its own: segment 1 + # exists once per circuit, so the pair with the service is the constraint above. - name: segment_sequence kind: Number - # 1 is not merely a value that loads. It is the correct reading of every - # path written before this feature: one segment of one. A path that was - # the whole route is segment 1, and stays it. default_value: 1 parameters: min_value: 1 - # A sanity ceiling in the manner of OtnPathHop.sequence, not a derived - # bound. Tying it to the section or the site count would make the - # bound wrong the moment the plant grows. + # A sanity ceiling, not a derived bound: tying it to the plant would date it. max_value: 50 optional: false description: Which segment of its circuit this path is. 1 for a circuit that spans one wavelength. order_weight: 1200 - # total_length_m, osnr_total_mdb, osnr_margin_mdb and latency_ns were - # measured over the same enumeration as total_loss_mdb below, and none of - # them needed moving. The extremes are 3450000 m against a cap of - # 10000000, OSNR 19045 to 36243 against 0 to 100000, margin -6455 to - # +16243 against -100000 to +100000, and 16902741 ns against 1000000000. - name: total_length_m kind: Number parameters: @@ -3543,23 +3042,12 @@ nodes: optional: true description: Route length in km. Read-only, derived from total_length_m. order_weight: 1510 - # 1000000 is 1000 dB, the measured worst case rounded up rather than a - # guess. The traversal returns routes of at most four sections, and every - # such route on the shipped plant was budgeted both ways: the largest loss - # is Madrid to Vienna at 854295, over 3450 km. The old 500000 refused that - # at write time with "854295 is higher than the maximum allowed value", - # and it already refused Madrid to Warsaw unregenerated at 737146, the - # route the regeneration scenario exists to fail. A cap that rejects a - # route the model is supposed to report as too lossy hides the negative - # result behind a write error. + # 1000000 is 1000 dB. A cap that rejects a route the model is supposed to + # report as too lossy would hide the negative result behind a write error. - name: total_loss_mdb kind: Number parameters: min_value: 0 - # A regenerated route is one path per segment, each carrying its own - # loss, so a cut lowers the figure: Madrid to Warsaw cut at Frankfurt - # is 462196 and 281950. The bound comes from the unregenerated route, - # never from a segment. max_value: 1000000 optional: false description: End-to-end loss in millidecibels, ageing allowance included. @@ -3629,32 +3117,14 @@ nodes: description: One-way latency in microseconds. Read-only, derived from latency_ns. order_weight: 1910 relationships: - # Mandatory. A path with no service has no meaning in this model: it is - # one wavelength's route chosen to answer one customer's request, and the - # figures on it were computed from that request's rate and mode. - # - # It was optional, on the stated grounds that a path is written before it - # is attached and that the generator's refusal path leaves paths behind. - # Both were measured false. `generators/optical_service.py` passes - # `"service": str(service["id"])` inside the same `client.create` call - # that makes the path, so a path never exists unattached even for one - # round trip, and feature 016 made the refusal path create nothing at all. + # Mandatory. A path with no service has no meaning here: it is one wavelength's + # route chosen to answer one customer's request, and its figures come from that. - name: service peer: OtnService kind: Attribute cardinality: one - # The migration was measured before the change, not reasoned about: 17 - # OtnOpticalPath records across the seven branches on the instance, 0 of - # them with no service, so nothing exists for a mandatory relationship - # to fail against. Had any been found, this would have stayed optional - # and the uniqueness constraint above would not exist. See T009a. - # - # It is also what that constraint needs. A constraint naming a - # relationship is rejected while the relationship is optional, with - # `cannot use relationship, relationship must be - # mandatory`. So the mandatory flag is not the schema paying for the - # constraint: the modelling argument stands on its own and the - # constraint is what it buys. + # Also what the uniqueness constraint above needs: a constraint naming an + # optional relationship is rejected with "relationship must be mandatory". optional: false identifier: otn_service__optical_path on_delete: no-action @@ -3667,11 +3137,8 @@ nodes: identifier: otn_carrier__optical_path on_delete: no-action order_weight: 810 - # `on_delete: cascade`, one of the two in the repository. The other is - # `OtnGenericDevice.ports` in `schemas/otn_base.yml`, and both are the same - # argument: a hop has no meaning apart from its path, a port has none apart - # from its device. Here the alternative is twenty-four orphans per deleted - # path. + # `on_delete: cascade`, one of two in this file, on the same argument as + # `OtnGenericDevice.ports`: a hop has no meaning apart from its path. - name: hops peer: OtnPathHop kind: Component @@ -3727,24 +3194,13 @@ nodes: optional: true description: Kilometres travelled. Read-only, derived from cumulative_length_m. order_weight: 1510 - # 1000000, the same cap as OtnOpticalPath.total_loss_mdb, and it has to be - # the same number rather than merely a large one. This attribute is that - # one's running total: the last hop's cumulative loss IS the route total, - # so a hop cap below the path cap refuses a write the path would accept. - # - # It was below it. T026c raised the path to 1000000 against a measured - # worst case of 854295 and left this at 500000, which refused 224 of the - # 830 route-directions the traversal can build on the shipped plant, over - # 122 distinct routes. The write failed on the hop rather than on the - # path, which is the wrong place to read the error and says nothing about - # the route being too lossy. `cumulative_length_m` and `total_length_m` - # were already paired at 10000000; only the loss pair had drifted. + # 1000000, the same cap as OtnOpticalPath.total_loss_mdb, and it has to be the + # same number: the last hop's cumulative loss IS the route total, so a lower cap + # here would refuse a write the path would accept. - name: cumulative_loss_mdb kind: Number parameters: min_value: 0 - # tests/unit/test_schema_contract.py asserts the pairing, so the next - # change to either cap fails rather than diverging in silence. max_value: 1000000 optional: false description: Loss accumulated at the output of this element, in millidecibels. @@ -3759,8 +3215,7 @@ nodes: optional: true description: Accumulated loss in dB. Read-only, derived from cumulative_loss_mdb. order_weight: 1610 - # Optional because OSNR is undefined until the first amplifier has added - # noise to measure it against. The engine returns None for those hops. + # Optional because OSNR is undefined until the first amplifier has added noise. - name: cumulative_osnr_mdb kind: Number parameters: @@ -3805,10 +3260,8 @@ nodes: optional: false identifier: otn_path__hops order_weight: 800 - # One relationship covers ROADMs, amplifiers and fiber spans. There is no - # ingress or egress port beside it: the loaded plant has no port-level - # adjacency between a span and the amplifier that feeds it, so both would - # be null on every span hop and read by nothing. + # One relationship covers ROADMs, amplifiers and fiber spans. No ingress or + # egress port beside it: the plant records no port-level adjacency to a span. - name: element peer: OtnOpticalElement kind: Attribute @@ -3818,24 +3271,15 @@ nodes: on_delete: no-action order_weight: 810 - # Six EuroHPC facilities sit on six of the fourteen PoPs. Until this kind - # existed the only record of that was the text after `eurohpc-` in a - # BuiltinTag name, read back by a helper duplicated in two transforms. The - # tag read was defended on a distinction that still holds: it read a tag, not - # a device, and nothing about an amplifier, a ROADM or a router is recovered - # from a name anywhere in this repository. - # - # The reversal is about something else. The failure was silent in both - # directions, measured on a probe branch: renaming `eurohpc-vega` dropped - # Vega from both maps and raised nothing, and a mistyped `eurohpc_deucalion` - # made a facility no map draws and no check reports. An edge fails loudly. + # Location. + # A facility on a site, as an edge rather than a suffix inside a tag name. An edge + # fails loudly: a renamed or mistyped tag dropped a facility and raised nothing. - name: Facility namespace: Otn description: A supercomputing facility hosted at a PoP. label: Facility icon: ri:cpu-line - # Reached through the site it sits on, like every other kind here, so it - # stays out of the automatic sidebar. menus/otn.yml carries the entries. + # Reached through the site it sits on, like every other kind here. include_in_menu: false human_friendly_id: - name__value @@ -3843,21 +3287,15 @@ nodes: - name__value display_label: name__value attributes: - # The tag suffix, and it has to stay the tag suffix: mapchrome.py - # upper-cases this string for the caption and the committed golden holds - # the upper-cased suffix. So `marenostrum-5`, never `MareNostrum 5`. + # Lower case with hyphens, so `marenostrum-5` and never `MareNostrum 5`. - name: name kind: Text unique: true order_weight: 1000 - # What a tag could never carry, and half the reason this is a kind. - name: description kind: Text optional: true order_weight: 1200 - # The six `eurohpc-` tags stay on their sites. They are data an operator - # wrote and a sweep does not delete data. They stop being read, which was - # the whole finding. relationships: - name: site peer: LocationSite @@ -3868,12 +3306,9 @@ nodes: on_delete: no-action order_weight: 900 -# LocationSite carries the OTN site fields rather than a second site kind. Three -# of OtnSite's attributes and four of its relationships move here. Its -# `facility` relationship is renamed `otn_facility`, because LocationSite -# already has a `facility` Text attribute and a node cannot hold both under one -# name. Its `CoreArtifactTarget` inheritance is dropped, because an extensions -# block cannot add to inherit_from and this library ships no generators. +# LocationSite carries the OTN site fields rather than a second site kind. Its +# `facility` relationship is renamed `otn_facility`, because LocationSite already +# has a `facility` Text attribute and a node cannot hold both under one name. extensions: nodes: - kind: LocationSite From d2adde3e8d57ad7311d2bd42292aa9e943437b79 Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 12:32:46 +0200 Subject: [PATCH 10/31] refactor(otn)!: drop state this library cannot produce The monitor kinds modelled readings, not intended state, and inherited OtnGenericPort so a device's ports list mixed interfaces with measurement snapshots. The optical path and hop budget fields were an optical planning tool's output, mandatory and recomputed whenever a span loss changed. The service refusal fields were a provisioning workflow no generator here runs. None of it had a producer in this library. OtnService.customer now points at OrganizationGeneric rather than carrying a free-text name. BREAKING CHANGE: removes OtnMonitor, OtnChannelMonitor, OtnAmplifierMonitor, OtnRoadmDegreeMonitor, OtnMuxDemuxMonitor, OtnRamanMonitor and OtnReceiverMonitor, and changes OtnService.customer from an attribute to a relationship. --- extensions/otn/otn.yml | 513 ++--------------------------------------- 1 file changed, 16 insertions(+), 497 deletions(-) diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index afab0497..ed48adae 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -477,47 +477,6 @@ generics: optional: false order_weight: 1900 - # Monitor generics. The concrete monitors list both of these plus OtnGenericPort, - # whose (device, name) constraint then binds across all five taken together. - - name: Monitor - namespace: Otn - description: The one fact every monitoring interface carries, whatever it measures. - label: Monitor - icon: mdi:gauge - include_in_menu: false - attributes: - - name: measured_at - kind: DateTime - optional: false - description: When this reading was taken. - order_weight: 1110 - - - name: ChannelMonitor - namespace: Otn - description: What a device that sees a band of channels reports about that band. - label: Channel monitor - icon: mdi:chart-histogram - include_in_menu: false - # A ROADM degree and a multiplexer report the same two numbers, so they are - # declared once here. The kinds stay distinct so a query can say which it means. - attributes: - - name: total_power_mdbm - kind: Number - parameters: - min_value: -40000 - max_value: 30000 - optional: false - description: Total power across all channels present, in millidecibel-milliwatts. - order_weight: 1300 - - name: channel_count - kind: Number - parameters: - min_value: 0 - max_value: 96 - optional: false - description: How many channels the monitor currently sees. - order_weight: 1310 - nodes: # Ports. - name: RouterPort @@ -933,233 +892,6 @@ nodes: - OtnGenericPort - OtnCopperPort - # The five monitors. Eleven of the fourteen readings belong to exactly one family, - # so five kinds can refuse both a missing reading and an impossible one. - - name: AmplifierMonitor - # Nothing constrains which device kind a monitor hangs off: a concrete kind - # cannot narrow the peer of a relationship it inherits from OtnGenericPort. - namespace: Otn - description: The last reading taken from an amplifier's monitoring interface. - label: Amplifier monitor - icon: mdi:gauge - include_in_menu: false - inherit_from: - - OtnGenericPort - - OtnMonitor - order_by: - - name__value - attributes: - - name: input_power_mdbm - kind: Number - parameters: - min_value: -40000 - max_value: 30000 - optional: false - description: Total input power in millidecibel-milliwatts. -3.5 dBm is -3500. - order_weight: 1200 - - name: input_power_display - kind: Text - read_only: true - computed_attribute: - kind: Jinja2 - jinja2_template: >- - {% if input_power_mdbm__value is not none %}{{ input_power_mdbm__value / 1000 }} dBm{% endif %} - optional: true - description: Input power in dBm. Read-only, derived from input_power_mdbm. - order_weight: 1210 - - name: output_power_mdbm - kind: Number - parameters: - min_value: -40000 - max_value: 30000 - optional: false - description: Total output power in millidecibel-milliwatts. - order_weight: 1220 - - name: output_power_display - kind: Text - read_only: true - computed_attribute: - kind: Jinja2 - jinja2_template: >- - {% if output_power_mdbm__value is not none %}{{ output_power_mdbm__value / 1000 }} dBm{% endif %} - optional: true - description: Output power in dBm. Read-only, derived from output_power_mdbm. - order_weight: 1230 - # Declared here and on OtnRamanMonitor rather than on OtnMonitor, which would - # put a gain field on the three monitor kinds that cannot produce one. - - name: measured_gain_mdb - kind: Number - parameters: - min_value: 0 - max_value: 40000 - optional: false - description: Gain the amplifier is actually delivering, in millidecibels. - order_weight: 1240 - - name: measured_gain_display - kind: Text - read_only: true - computed_attribute: - kind: Jinja2 - jinja2_template: >- - {% if measured_gain_mdb__value is not none %}{{ measured_gain_mdb__value / 1000 }} dB{% endif %} - optional: true - description: Measured gain in dB. Read-only, derived from measured_gain_mdb. - order_weight: 1250 - - name: tilt_mdb - kind: Number - parameters: - min_value: -5000 - max_value: 5000 - optional: false - description: Gain tilt across the band in millidecibels. Negative tilts the red end down. - order_weight: 1260 - - # No attributes of its own: both readings come from OtnChannelMonitor. - - name: RoadmDegreeMonitor - namespace: Otn - description: The last reading taken from a ROADM degree's monitoring interface. - label: ROADM degree monitor - icon: mdi:gauge - include_in_menu: false - inherit_from: - - OtnGenericPort - - OtnMonitor - - OtnChannelMonitor - order_by: - - name__value - - - name: MuxDemuxMonitor - namespace: Otn - description: The last reading taken from a multiplexer's monitoring interface. - label: Mux/demux monitor - icon: mdi:gauge - include_in_menu: false - inherit_from: - - OtnGenericPort - - OtnMonitor - - OtnChannelMonitor - order_by: - - name__value - - - name: RamanMonitor - namespace: Otn - description: The last reading taken from a Raman pump's monitoring interface. - label: Raman monitor - icon: mdi:gauge - include_in_menu: false - inherit_from: - - OtnGenericPort - - OtnMonitor - order_by: - - name__value - attributes: - - name: measured_gain_mdb - kind: Number - parameters: - min_value: 0 - max_value: 40000 - optional: false - description: Gain the amplifier is actually delivering, in millidecibels. - order_weight: 1240 - - name: measured_gain_display - kind: Text - read_only: true - computed_attribute: - kind: Jinja2 - jinja2_template: >- - {% if measured_gain_mdb__value is not none %}{{ measured_gain_mdb__value / 1000 }} dB{% endif %} - optional: true - description: Measured gain in dB. Read-only, derived from measured_gain_mdb. - order_weight: 1250 - - name: pump_power_mdbm - kind: Number - parameters: - min_value: 0 - max_value: 40000 - optional: false - description: Raman pump launch power in millidecibel-milliwatts. - order_weight: 1400 - - name: back_reflection_mdb - kind: Number - parameters: - min_value: 0 - max_value: 60000 - optional: false - description: Return loss seen by the pump, in millidecibels. A safety interlock reading. - order_weight: 1410 - - - name: ReceiverMonitor - namespace: Otn - description: The last reading taken from a transponder receiver's monitoring interface. - label: Receiver monitor - icon: mdi:gauge - include_in_menu: false - inherit_from: - - OtnGenericPort - - OtnMonitor - order_by: - - name__value - attributes: - # OSNR sits on no other monitor kind: a coherent receiver's DSP computes it. - - name: measured_osnr_mdb - kind: Number - parameters: - min_value: 0 - max_value: 50000 - optional: false - description: OSNR the receiver reports, in millidecibels. 24.1 dB is 24100. - order_weight: 1500 - - name: measured_osnr_display - kind: Text - read_only: true - computed_attribute: - kind: Jinja2 - jinja2_template: >- - {% if measured_osnr_mdb__value is not none %}{{ measured_osnr_mdb__value / 1000 }} dB{% endif %} - optional: true - description: Measured OSNR in dB. Read-only, derived from measured_osnr_mdb. - order_weight: 1510 - - name: rx_power_mdbm - kind: Number - parameters: - min_value: -40000 - max_value: 10000 - optional: false - description: Received power at the coherent front end, in millidecibel-milliwatts. - order_weight: 1520 - - name: pre_fec_ber_ppb - kind: Number - parameters: - min_value: 0 - max_value: 1000000000 - optional: false - description: Pre-FEC bit error rate in parts per billion. 2.1e-3 is 2100000. - order_weight: 1530 - - name: q_factor_mdb - kind: Number - parameters: - min_value: 0 - max_value: 30000 - optional: false - description: Q factor in millidecibels. - order_weight: 1540 - - name: cd_fs_per_nm - kind: Number - parameters: - min_value: -100000000 - max_value: 100000000 - optional: false - description: Chromatic dispersion the receiver compensated, in femtoseconds per nanometre. - order_weight: 1550 - - name: dgd_fs - kind: Number - parameters: - min_value: 0 - max_value: 200000 - optional: false - description: Differential group delay in femtoseconds. - order_weight: 1560 - # The catalog entry and the physical unit. Neither is racked, so neither inherits. - name: TransceiverType namespace: Otn @@ -1625,7 +1357,7 @@ nodes: order_by: - name__value display_label: name__value - # No total_length_m, no total_loss_mdb, no span_count and no hop_count. + # No cached total length, total loss, span_count or hop_count. # Every reader sums them over the spans instead. attributes: - name: name @@ -2470,7 +2202,7 @@ nodes: optional: false description: Whether the device carries the whole payload through or demultiplexes and regroups containers. order_weight: 1300 - # Nanoseconds, the unit fec_latency_ns and latency_ns already use. No paired + # Nanoseconds, the unit fec_latency_ns already uses. No paired # _display: the whole plausible range is hundreds of nanoseconds. - name: framing_latency_ns kind: Number @@ -2723,13 +2455,6 @@ nodes: max_length: 256 optional: true order_weight: 1100 - - name: customer - kind: Text - parameters: - max_length: 64 - optional: false - description: Who bought it. A Text field, because this model has no organisation kind. - order_weight: 1200 # Customer intent, not a modulation format. The mode chosen to meet it lands on # the carrier. - name: rate_gbps @@ -2821,56 +2546,6 @@ nodes: optional: true description: Latency budget in microseconds. Read-only, derived from max_latency_ns. order_weight: 1710 - # A coded refusal rather than a free string, so a typo cannot become a seventh - # reason nobody reads. The colours group them: grey is unanswerable, red is the - # physics refusing a route that exists, amber is the network full. - - name: rejection_code - kind: Dropdown - choices: - - name: no-route - label: No route - description: No path exists between the two endpoints. - color: "#9e9e9e" - - name: no-mode - label: No mode - description: No transponder mode meets the requested rate. - color: "#757575" - - name: budget - label: Optical budget - description: A route exists and the OSNR margin is negative on all of them. - color: "#f44336" - - name: latency - label: Latency budget - description: A route exists and every one of them is slower than the service allows. - color: "#b71c1c" - - name: capacity - label: No spectrum - description: A route exists and no channel on it is free. - color: "#ff9800" - - name: no-slots - label: No tributary slots - description: A wavelength was found and its containers have no room for the client. - color: "#ffb74d" - optional: true - description: Why the service was refused, as one of six codes. Empty whenever a path exists. - order_weight: 1801 - # The prose half of the refusal, never parsed. 512 is what the old single - # string carried. - - name: rejection_detail - kind: Text - parameters: - max_length: 512 - optional: true - description: What the refusal looked like in detail. The prose beside the code, never parsed. - order_weight: 1802 - # `optional: false` with `default_value: false` is what makes this safe to add - # to a kind that already has rows, and false is the right reading of each. - - name: refusal_accepted - kind: Boolean - default_value: false - optional: false - description: A person read the refusal and accepted it. Only a person sets this. - order_weight: 1803 relationships: # Peered at the generic, not at OtnRouter, so a service can terminate on a # transponder. One-sided: "which services land here" is a filter on this side. @@ -2933,6 +2608,18 @@ nodes: identifier: otn_service__containers on_delete: no-action order_weight: 920 + # Peered at the generic, so a deployment can point at whatever organization + # kind it has, including OrganizationTenant from extensions/tenancy, without + # this extension depending on that one. + - name: customer + peer: OrganizationGeneric + label: Customer + kind: Attribute + cardinality: one + optional: false + identifier: otn_service__customer + on_delete: no-action + order_weight: 1200 # A node, not a Text attribute and not a Dropdown. Small on purpose: everything # about which routes are actually disjoint is computed from the spans. @@ -2982,7 +2669,7 @@ nodes: - name: OpticalPath namespace: Otn - description: The materialised chosen route for one service, with the budget that chose it. + description: The materialised chosen route for one service. label: Optical path icon: mdi:map-marker-path include_in_menu: false @@ -3024,98 +2711,6 @@ nodes: optional: false description: Which segment of its circuit this path is. 1 for a circuit that spans one wavelength. order_weight: 1200 - - name: total_length_m - kind: Number - parameters: - min_value: 0 - max_value: 10000000 - optional: false - description: Route length in metres, summed over every span on the path. - order_weight: 1500 - - name: total_length_display - kind: Text - read_only: true - computed_attribute: - kind: Jinja2 - jinja2_template: >- - {% if total_length_m__value is not none %}{{ total_length_m__value / 1000 }} km{% endif %} - optional: true - description: Route length in km. Read-only, derived from total_length_m. - order_weight: 1510 - # 1000000 is 1000 dB. A cap that rejects a route the model is supposed to - # report as too lossy would hide the negative result behind a write error. - - name: total_loss_mdb - kind: Number - parameters: - min_value: 0 - max_value: 1000000 - optional: false - description: End-to-end loss in millidecibels, ageing allowance included. - order_weight: 1600 - - name: total_loss_display - kind: Text - read_only: true - computed_attribute: - kind: Jinja2 - jinja2_template: >- - {% if total_loss_mdb__value is not none %}{{ total_loss_mdb__value / 1000 }} dB{% endif %} - optional: true - description: End-to-end loss in dB. Read-only, derived from total_loss_mdb. - order_weight: 1610 - - name: osnr_total_mdb - kind: Number - parameters: - min_value: 0 - max_value: 100000 - optional: false - description: OSNR delivered at the receiver in millidecibels, over the amplifier cascade. - order_weight: 1700 - - name: osnr_total_display - kind: Text - read_only: true - computed_attribute: - kind: Jinja2 - jinja2_template: >- - {% if osnr_total_mdb__value is not none %}{{ osnr_total_mdb__value / 1000 }} dB{% endif %} - optional: true - description: Delivered OSNR in dB. Read-only, derived from osnr_total_mdb. - order_weight: 1710 - - name: osnr_margin_mdb - kind: Number - parameters: - min_value: -100000 - max_value: 100000 - optional: false - description: Delivered OSNR less the mode requirement and the system margin, in millidecibels. - order_weight: 1800 - - name: osnr_margin_display - kind: Text - read_only: true - computed_attribute: - kind: Jinja2 - jinja2_template: >- - {% if osnr_margin_mdb__value is not none %}{{ osnr_margin_mdb__value / 1000 }} dB{% endif %} - optional: true - description: Signed OSNR margin in dB. Read-only, derived from osnr_margin_mdb. - order_weight: 1810 - - name: latency_ns - kind: Number - parameters: - min_value: 0 - max_value: 1000000000 - optional: false - description: One-way latency in nanoseconds. Propagation, node, amplifier and FEC terms. - order_weight: 1900 - - name: latency_display - kind: Text - read_only: true - computed_attribute: - kind: Jinja2 - jinja2_template: >- - {% if latency_ns__value is not none %}{{ latency_ns__value / 1000 }} us{% endif %} - optional: true - description: One-way latency in microseconds. Read-only, derived from latency_ns. - order_weight: 1910 relationships: # Mandatory. A path with no service has no meaning here: it is one wavelength's # route chosen to answer one customer's request, and its figures come from that. @@ -3150,7 +2745,7 @@ nodes: - name: PathHop namespace: Otn - description: One ordered element on a path, carrying the budget's running totals. + description: One ordered element on a path. label: Path hop icon: mdi:ray-vertex include_in_menu: false @@ -3176,82 +2771,6 @@ nodes: optional: false description: Position along the path, counting from the endpoint A ROADM. order_weight: 1100 - - name: cumulative_length_m - kind: Number - parameters: - min_value: 0 - max_value: 10000000 - optional: false - description: Metres travelled at the output of this element. - order_weight: 1500 - - name: cumulative_length_display - kind: Text - read_only: true - computed_attribute: - kind: Jinja2 - jinja2_template: >- - {% if cumulative_length_m__value is not none %}{{ cumulative_length_m__value / 1000 }} km{% endif %} - optional: true - description: Kilometres travelled. Read-only, derived from cumulative_length_m. - order_weight: 1510 - # 1000000, the same cap as OtnOpticalPath.total_loss_mdb, and it has to be the - # same number: the last hop's cumulative loss IS the route total, so a lower cap - # here would refuse a write the path would accept. - - name: cumulative_loss_mdb - kind: Number - parameters: - min_value: 0 - max_value: 1000000 - optional: false - description: Loss accumulated at the output of this element, in millidecibels. - order_weight: 1600 - - name: cumulative_loss_display - kind: Text - read_only: true - computed_attribute: - kind: Jinja2 - jinja2_template: >- - {% if cumulative_loss_mdb__value is not none %}{{ cumulative_loss_mdb__value / 1000 }} dB{% endif %} - optional: true - description: Accumulated loss in dB. Read-only, derived from cumulative_loss_mdb. - order_weight: 1610 - # Optional because OSNR is undefined until the first amplifier has added noise. - - name: cumulative_osnr_mdb - kind: Number - parameters: - min_value: 0 - max_value: 100000 - optional: true - description: OSNR after this element, in millidecibels. Empty ahead of the first amplifier. - order_weight: 1700 - - name: cumulative_osnr_display - kind: Text - read_only: true - computed_attribute: - kind: Jinja2 - jinja2_template: >- - {% if cumulative_osnr_mdb__value is not none %}{{ cumulative_osnr_mdb__value / 1000 }} dB{% endif %} - optional: true - description: Accumulated OSNR in dB. Empty ahead of the first amplifier. - order_weight: 1710 - - name: cumulative_delay_ns - kind: Number - parameters: - min_value: 0 - max_value: 1000000000 - optional: false - description: One-way delay at the output of this element, in nanoseconds. - order_weight: 1800 - - name: cumulative_delay_display - kind: Text - read_only: true - computed_attribute: - kind: Jinja2 - jinja2_template: >- - {% if cumulative_delay_ns__value is not none %}{{ cumulative_delay_ns__value / 1000 }} us{% endif %} - optional: true - description: Accumulated delay in microseconds. Read-only, derived from cumulative_delay_ns. - order_weight: 1810 relationships: - name: path peer: OtnOpticalPath From 0c526c06ab937841eaf71d22e535dee87b57e9c2 Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 12:35:41 +0200 Subject: [PATCH 11/31] docs(otn): tidy comments left behind by the cleanup passes Two comments opened by restating the description below them, one cited the demo's design history, and the inverse-relationship rule sat below the identifiers it explains. The variable attenuator note pointed its relative clause at the wrong attribute. --- extensions/otn/otn.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index ed48adae..396d72b7 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -8,6 +8,9 @@ # Optical transport network. Ported from opsmill/infrahub-demo-otn at e98be9b. # Devices here are optical roles; OtnGenericDevice.dcim_device links one to its # DcimPhysicalDevice record. See the reference page for what is out of scope. +# A `cardinality: one` relationship earns an inverse only where the peer's own page +# is what the reverse list is for; otherwise use a server-side filter on the forward +# side. An inverse must reuse the forward identifier, or it reads an empty list. # --------------------------------------------------------------------------- version: "1.0" @@ -282,7 +285,8 @@ generics: # An OTN device names the optical role; DcimPhysicalDevice names the physical # asset. This edge joins the two records for one box rather than duplicating # it, and it does not merge the two graphs: no OTN port is a DcimEndpoint, so - # extensions/cable still cannot terminate on optical gear. + # extensions/cable still cannot terminate on optical gear. Optional, because a + # deployment with no Dcim inventory is equally valid. - name: dcim_device peer: DcimPhysicalDevice label: Physical device record @@ -1048,9 +1052,7 @@ nodes: order_weight: 950 # Plant. - # A `cardinality: one` relationship earns an inverse only where the peer's own page - # is what the reverse list is for; otherwise use a server-side filter on the forward - # side. An inverse must reuse the forward identifier, or it reads an empty list. + - name: FiberType namespace: Otn description: Single-mode fiber family. Attenuation, dispersion and group index at 1550 nm. @@ -1705,8 +1707,6 @@ nodes: optional: false description: Grouping the catalog by layer is the first thing a reader does. order_weight: 1200 - # Per signal, where the rule it replaces was per layer: two rows on one layer - # may differ. - name: auto_selectable kind: Boolean # Fails closed: a row added without a decision is unreachable by the @@ -1847,9 +1847,8 @@ nodes: max_length: 256 optional: true order_weight: 1300 - # Which segment of its circuit this container rides. A circuit is its - # containers ordered by this, as its wavelengths are its paths ordered by - # OtnOpticalPath.segment_sequence. + # A circuit is its containers ordered by this, as its wavelengths are its + # paths ordered by OtnOpticalPath.segment_sequence. - name: segment_sequence kind: Number default_value: 1 @@ -2290,8 +2289,8 @@ nodes: description: Attenuation in dB. Read-only, derived from attenuation_mdb. order_weight: 1910 # The absolute ceiling is on the attribute, so the schema refuses a physically - # impossible figure at write time. It cannot refuse a setting past this device's - # own maximum, which is a sibling attribute's value. Unenforced here. + # impossible figure at write time. It cannot refuse a setting past this maximum, + # because the setting is a sibling attribute's value. Unenforced here. - name: max_attenuation_mdb kind: Number parameters: @@ -2699,8 +2698,8 @@ nodes: max_length: 256 optional: true order_weight: 1100 - # Which segment of its circuit this path is. Not unique on its own: segment 1 - # exists once per circuit, so the pair with the service is the constraint above. + # Not unique on its own: segment 1 exists once per circuit, so the pair with + # the service is the constraint above. - name: segment_sequence kind: Number default_value: 1 @@ -2791,6 +2790,7 @@ nodes: order_weight: 810 # Location. + # A facility on a site, as an edge rather than a suffix inside a tag name. An edge # fails loudly: a renamed or mistyped tag dropped a facility and raised nothing. - name: Facility From 7c89cc8f5a3ae52a4f269bae875afcee6a860247 Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 12:46:37 +0200 Subject: [PATCH 12/31] refactor(otn): omit on_delete where the default already says no-action The library declares on_delete only when the answer is cascade, so the exceptional cases are the ones a reader finds. This file stated the default 65 times, which hid the two relationships that really do cascade. --- extensions/otn/otn.yml | 65 ------------------------------------------ 1 file changed, 65 deletions(-) diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index 396d72b7..4422f7a6 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -133,7 +133,6 @@ generics: cardinality: one optional: true identifier: otn_port__connected_to - on_delete: no-action order_weight: 900 - name: OpticalElement @@ -280,7 +279,6 @@ generics: cardinality: one optional: true identifier: otn_site__devices - on_delete: no-action order_weight: 800 # An OTN device names the optical role; DcimPhysicalDevice names the physical # asset. This edge joins the two records for one box rather than duplicating @@ -294,7 +292,6 @@ generics: cardinality: one optional: true identifier: otn_device__dcim_device - on_delete: no-action order_weight: 940 - name: ports peer: OtnGenericPort @@ -545,7 +542,6 @@ nodes: cardinality: one optional: true identifier: otn_optical_port__transceiver - on_delete: no-action order_weight: 960 - name: ClientPort @@ -607,7 +603,6 @@ nodes: cardinality: one optional: true identifier: otn_optical_port__transceiver - on_delete: no-action order_weight: 960 - name: LinePort @@ -673,7 +668,6 @@ nodes: cardinality: one optional: true identifier: otn_carrier__line_ports - on_delete: no-action order_weight: 850 - name: transceiver peer: OtnTransceiver @@ -682,7 +676,6 @@ nodes: cardinality: one optional: true identifier: otn_optical_port__transceiver - on_delete: no-action order_weight: 960 - name: RoadmAddDropPort @@ -864,7 +857,6 @@ nodes: cardinality: one optional: true identifier: otn_frequency_grid__mux_client_ports - on_delete: no-action order_weight: 850 - name: cwdm_channel peer: OtnCwdmChannel @@ -873,7 +865,6 @@ nodes: cardinality: one optional: true identifier: otn_cwdm_channel__mux_client_ports - on_delete: no-action order_weight: 860 - name: MuxLinePort @@ -983,7 +974,6 @@ nodes: cardinality: many optional: true identifier: otn_optical_mode__transceiver_types - on_delete: no-action order_weight: 900 - name: Transceiver @@ -1033,7 +1023,6 @@ nodes: cardinality: one optional: false identifier: otn_transceiver_type__units - on_delete: no-action order_weight: 900 # Optional, so a spare, an RMA and a decommissioned unit stay modellable, and # therefore carrying no uniqueness constraint: Infrahub refuses one on an @@ -1048,7 +1037,6 @@ nodes: cardinality: one optional: true identifier: otn_optical_port__transceiver - on_delete: no-action order_weight: 950 # Plant. @@ -1136,7 +1124,6 @@ nodes: cardinality: many optional: true identifier: otn_fiber_type__spans - on_delete: no-action order_weight: 900 - name: Conduit @@ -1179,7 +1166,6 @@ nodes: cardinality: many optional: true identifier: otn_conduit__spans - on_delete: no-action order_weight: 900 - name: FiberSpan @@ -1292,7 +1278,6 @@ nodes: cardinality: one optional: false identifier: otn_fiber_type__spans - on_delete: no-action order_weight: 800 - name: site_a peer: LocationSite @@ -1300,7 +1285,6 @@ nodes: cardinality: one optional: false identifier: otn_span__site_a - on_delete: no-action order_weight: 810 - name: site_b peer: LocationSite @@ -1308,7 +1292,6 @@ nodes: cardinality: one optional: false identifier: otn_span__site_b - on_delete: no-action order_weight: 820 - name: conduit peer: OtnConduit @@ -1316,7 +1299,6 @@ nodes: cardinality: one optional: true identifier: otn_conduit__spans - on_delete: no-action order_weight: 830 - name: oms peer: OtnOpticalMultiplexSection @@ -1324,7 +1306,6 @@ nodes: cardinality: one optional: true identifier: otn_oms__spans - on_delete: no-action order_weight: 840 # The inverse of OtnRamanPump.span, on the same identifier. A budget walks a # section to its spans, so without this side no pump is visible on that walk. @@ -1334,7 +1315,6 @@ nodes: cardinality: many optional: true identifier: otn_span__raman_pumps - on_delete: no-action order_weight: 850 # The ports at each end of the glass. Declared on the span alone: the inverse # would put the field on every port kind and most ports terminate no span. @@ -1345,7 +1325,6 @@ nodes: cardinality: many optional: true identifier: otn_span__terminating_ports - on_delete: no-action order_weight: 950 - name: OpticalMultiplexSection @@ -1383,7 +1362,6 @@ nodes: cardinality: one optional: false identifier: otn_oms__roadm_a - on_delete: no-action order_weight: 800 - name: roadm_b peer: OtnRoadm @@ -1391,7 +1369,6 @@ nodes: cardinality: one optional: false identifier: otn_oms__roadm_b - on_delete: no-action order_weight: 810 - name: spans peer: OtnFiberSpan @@ -1399,7 +1376,6 @@ nodes: cardinality: many optional: true identifier: otn_oms__spans - on_delete: no-action order_weight: 900 # One relationship per direction of travel rather than one holding both chains, # so which chain an amplifier is in is stored by the graph. Both stay optional. @@ -1409,7 +1385,6 @@ nodes: cardinality: many optional: true identifier: otn_oms__amplifiers_a2b - on_delete: no-action order_weight: 910 - name: amplifiers_b2a peer: OtnAmplifier @@ -1417,7 +1392,6 @@ nodes: cardinality: many optional: true identifier: otn_oms__amplifiers_b2a - on_delete: no-action order_weight: 920 # Logical. @@ -1471,7 +1445,6 @@ nodes: cardinality: many optional: true identifier: otn_carrier__channel - on_delete: no-action order_weight: 900 # The coarse plan is a separate kind so that `OtnOpticalCarrier.channel`, which @@ -1656,7 +1629,6 @@ nodes: cardinality: many optional: true identifier: otn_carrier__optical_mode - on_delete: no-action order_weight: 900 - name: ClientSignal @@ -1777,7 +1749,6 @@ nodes: cardinality: many optional: true identifier: otn_client_signal__containers - on_delete: no-action order_weight: 900 # The inverse of OtnService.client_signal, on the same identifier. - name: services @@ -1786,7 +1757,6 @@ nodes: cardinality: many optional: true identifier: otn_service__client_signal - on_delete: no-action order_weight: 910 - name: Container @@ -1887,7 +1857,6 @@ nodes: cardinality: one optional: true identifier: otn_carrier__containers - on_delete: no-action order_weight: 790 - name: client_signal peer: OtnClientSignal @@ -1895,7 +1864,6 @@ nodes: cardinality: one optional: true identifier: otn_client_signal__containers - on_delete: no-action order_weight: 800 - name: parent_container peer: OtnContainer @@ -1904,7 +1872,6 @@ nodes: optional: true identifier: otn_container__children direction: outbound - on_delete: no-action order_weight: 810 # Only a client container carries one. A line container leaves it empty on # purpose: it belongs to a wavelength that several services may share. @@ -1917,7 +1884,6 @@ nodes: cardinality: one optional: true identifier: otn_service__containers - on_delete: no-action order_weight: 820 - name: child_containers peer: OtnContainer @@ -1926,7 +1892,6 @@ nodes: optional: true identifier: otn_container__children direction: inbound - on_delete: no-action order_weight: 900 # Devices. @@ -1968,7 +1933,6 @@ nodes: cardinality: many optional: true identifier: otn_oms__roadm_a - on_delete: no-action order_weight: 960 - name: sections_b peer: OtnOpticalMultiplexSection @@ -1977,7 +1941,6 @@ nodes: cardinality: many optional: true identifier: otn_oms__roadm_b - on_delete: no-action order_weight: 970 - name: Amplifier @@ -2056,7 +2019,6 @@ nodes: # Both optional, because an amplifier is creatable before its section exists. optional: true identifier: otn_oms__amplifiers_a2b - on_delete: no-action order_weight: 950 - name: oms_b2a peer: OtnOpticalMultiplexSection @@ -2064,7 +2026,6 @@ nodes: cardinality: one optional: true identifier: otn_oms__amplifiers_b2a - on_delete: no-action order_weight: 960 - name: MuxDemux @@ -2085,7 +2046,6 @@ nodes: cardinality: many optional: true identifier: otn_mux_demux__cwdm_channels - on_delete: no-action order_weight: 1900 - name: PatchPanel @@ -2169,7 +2129,6 @@ nodes: cardinality: one optional: false identifier: otn_span__raman_pumps - on_delete: no-action order_weight: 850 - name: OduSwitch @@ -2223,7 +2182,6 @@ nodes: cardinality: many optional: true identifier: otn_odu_switch__carriers - on_delete: no-action order_weight: 1900 # Two attenuator kinds rather than one carrying a type Dropdown and an optional @@ -2360,7 +2318,6 @@ nodes: cardinality: one optional: false identifier: otn_carrier__channel - on_delete: no-action order_weight: 800 - name: optical_mode peer: OtnOpticalMode @@ -2368,7 +2325,6 @@ nodes: cardinality: one optional: true identifier: otn_carrier__optical_mode - on_delete: no-action order_weight: 810 # Many, not one, because two services grooming into one line container both # ride this carrier. Each service still has exactly one path of its own. The @@ -2381,7 +2337,6 @@ nodes: # Not renamed to `optical_paths`: Infrahub reads the old and the new as two # relationships on one identifier and refuses the load. identifier: otn_carrier__optical_path - on_delete: no-action order_weight: 820 # Unordered: occupancy is set membership. Ordering along a route is on the hops. - name: sections @@ -2390,7 +2345,6 @@ nodes: cardinality: many optional: true identifier: otn_carrier__sections - on_delete: no-action order_weight: 900 # What the wavelength carries, so "what dies with this carrier" is a read of it. - name: containers @@ -2399,7 +2353,6 @@ nodes: cardinality: many optional: true identifier: otn_carrier__containers - on_delete: no-action order_weight: 910 # The inverse of OtnOduSwitch.carriers, both sides writing the identifier by # hand. Attachment, not termination: `line_ports` is the termination answer. @@ -2410,7 +2363,6 @@ nodes: cardinality: many optional: true identifier: otn_odu_switch__carriers - on_delete: no-action order_weight: 920 # The inverse of OtnLinePort.carrier, both sides writing the identifier by # hand. Many, because a wavelength is terminated at each of its two ends. @@ -2422,7 +2374,6 @@ nodes: cardinality: many optional: true identifier: otn_carrier__line_ports - on_delete: no-action order_weight: 930 # Service. @@ -2554,7 +2505,6 @@ nodes: cardinality: one optional: false identifier: otn_service__endpoint_a - on_delete: no-action order_weight: 800 - name: endpoint_z peer: OtnGenericDevice @@ -2562,7 +2512,6 @@ nodes: cardinality: one optional: false identifier: otn_service__endpoint_z - on_delete: no-action order_weight: 810 # Many, not one, because a circuit regenerated at an intermediate site is one # path per wavelength, each with its own budget. Order by segment_sequence. @@ -2572,7 +2521,6 @@ nodes: cardinality: many optional: true identifier: otn_service__optical_path - on_delete: no-action order_weight: 900 # Optional: a service naming no client signal is still provisionable from its # rate alone. @@ -2582,7 +2530,6 @@ nodes: cardinality: one optional: true identifier: otn_service__client_signal - on_delete: no-action order_weight: 910 # Two services pointing at one group declare that their routes must not share a # conduit. A relationship, not a string: an identifier either resolves or the @@ -2595,7 +2542,6 @@ nodes: identifier: otn_diversity_group__services # A group and a service are independent: deleting the group ends the # requirement. - on_delete: no-action order_weight: 915 # The client containers this circuit owns, written from the container side. A # deleted service leaves its containers behind, inert. @@ -2605,7 +2551,6 @@ nodes: cardinality: many optional: true identifier: otn_service__containers - on_delete: no-action order_weight: 920 # Peered at the generic, so a deployment can point at whatever organization # kind it has, including OrganizationTenant from extensions/tenancy, without @@ -2617,7 +2562,6 @@ nodes: cardinality: one optional: false identifier: otn_service__customer - on_delete: no-action order_weight: 1200 # A node, not a Text attribute and not a Dropdown. Small on purpose: everything @@ -2663,7 +2607,6 @@ nodes: cardinality: many optional: true identifier: otn_diversity_group__services - on_delete: no-action order_weight: 800 - name: OpticalPath @@ -2721,7 +2664,6 @@ nodes: # optional relationship is rejected with "relationship must be mandatory". optional: false identifier: otn_service__optical_path - on_delete: no-action order_weight: 800 - name: carrier peer: OtnOpticalCarrier @@ -2729,7 +2671,6 @@ nodes: cardinality: one optional: true identifier: otn_carrier__optical_path - on_delete: no-action order_weight: 810 # `on_delete: cascade`, one of two in this file, on the same argument as # `OtnGenericDevice.ports`: a hop has no meaning apart from its path. @@ -2786,7 +2727,6 @@ nodes: cardinality: one optional: false identifier: otn_hop__element - on_delete: no-action order_weight: 810 # Location. @@ -2822,7 +2762,6 @@ nodes: cardinality: one optional: true identifier: otn_site__facility - on_delete: no-action order_weight: 900 # LocationSite carries the OTN site fields rather than a second site kind. Its @@ -2868,7 +2807,6 @@ extensions: cardinality: one optional: true identifier: otn_site__facility - on_delete: no-action order_weight: 930 - name: devices peer: OtnGenericDevice @@ -2876,7 +2814,6 @@ extensions: cardinality: many optional: true identifier: otn_site__devices - on_delete: no-action order_weight: 900 - name: spans_a peer: OtnFiberSpan @@ -2885,7 +2822,6 @@ extensions: cardinality: many optional: true identifier: otn_span__site_a - on_delete: no-action order_weight: 910 - name: spans_b peer: OtnFiberSpan @@ -2894,5 +2830,4 @@ extensions: cardinality: many optional: true identifier: otn_span__site_b - on_delete: no-action order_weight: 920 From 99ab012c9b2299ddcb2a244595a80385c9d0457f Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 12:49:19 +0200 Subject: [PATCH 13/31] fix(otn): own the optical path from its service OtnOpticalPath.service was mandatory but modelled as a cross-reference, so deleting a service left its paths behind with the link unset. Component and Parent with cascade makes the ownership explicit, and the delete now reaches the hops each path already owns. --- extensions/otn/otn.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index 4422f7a6..bb74aa5f 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -2515,12 +2515,15 @@ nodes: order_weight: 810 # Many, not one, because a circuit regenerated at an intermediate site is one # path per wavelength, each with its own budget. Order by segment_sequence. + # Component, with cascade: the path is owned by its service, so removing the + # service does not leave a mandatory link dangling. - name: optical_path peer: OtnOpticalPath - kind: Attribute + kind: Component cardinality: many optional: true identifier: otn_service__optical_path + on_delete: cascade order_weight: 900 # Optional: a service naming no client signal is still provisionable from its # rate alone. @@ -2658,7 +2661,7 @@ nodes: # route chosen to answer one customer's request, and its figures come from that. - name: service peer: OtnService - kind: Attribute + kind: Parent cardinality: one # Also what the uniqueness constraint above needs: a constraint naming an # optional relationship is rejected with "relationship must be mandatory". @@ -2672,8 +2675,8 @@ nodes: optional: true identifier: otn_carrier__optical_path order_weight: 810 - # `on_delete: cascade`, one of two in this file, on the same argument as - # `OtnGenericDevice.ports`: a hop has no meaning apart from its path. + # `on_delete: cascade`, one of three in this file (with `OtnGenericDevice.ports` + # and `OtnService.optical_path`): a hop has no meaning apart from its path. - name: hops peer: OtnPathHop kind: Component From aa4fd9b24b560b4047eb8f9b11bc01422dc0b7e6 Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 13:01:15 +0200 Subject: [PATCH 14/31] feat(otn): surface the browsable kinds in the automatic sidebar --- extensions/otn/otn.yml | 59 +++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index bb74aa5f..f43ac000 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -217,7 +217,8 @@ generics: description: Anything racked at a site. label: Device icon: mdi:server - include_in_menu: false + # Carries the Devices entry in the sidebar. The ten device kinds nest under it. + include_in_menu: true human_friendly_id: - name__value order_by: @@ -893,7 +894,7 @@ nodes: description: A pluggable optic part number and what it can be made to do. label: Transceiver type icon: mdi:chip - include_in_menu: false + include_in_menu: true human_friendly_id: - part_number__value order_by: @@ -981,7 +982,7 @@ nodes: description: One physical pluggable optic, fitted in a port or sitting on a shelf. label: Transceiver icon: mdi:memory - include_in_menu: false + include_in_menu: true human_friendly_id: - serial__value order_by: @@ -1046,7 +1047,7 @@ nodes: description: Single-mode fiber family. Attenuation, dispersion and group index at 1550 nm. label: Fiber type icon: mdi:cable-data - include_in_menu: false + include_in_menu: true human_friendly_id: - name__value order_by: @@ -1131,7 +1132,7 @@ nodes: description: Shared-risk link group. Two routes through one conduit are not diverse. label: Conduit icon: mdi:pipe - include_in_menu: false + include_in_menu: true human_friendly_id: - name__value order_by: @@ -1173,7 +1174,7 @@ nodes: description: One amplifier-to-amplifier fiber section. Lossy, but not racked and not a device. label: Fiber span icon: mdi:vector-line - include_in_menu: false + include_in_menu: true inherit_from: - OtnOpticalElement human_friendly_id: @@ -1332,7 +1333,7 @@ nodes: description: ROADM to ROADM. Groups the ordered spans and inline amplifiers between two degrees. label: Optical multiplex section icon: mdi:ray-start-end - include_in_menu: false + include_in_menu: true human_friendly_id: - name__value order_by: @@ -1400,7 +1401,7 @@ nodes: description: One ITU-T G.694.1 channel. 50 GHz fixed grid, 191.35 to 196.10 THz. label: Frequency grid channel icon: mdi:sine-wave - include_in_menu: false + include_in_menu: true human_friendly_id: - channel_number__value order_by: @@ -1454,7 +1455,7 @@ nodes: description: One ITU-T G.694.2 coarse wavelength. 20 nm spacing, 1271 to 1611 nm. label: CWDM wavelength icon: mdi:palette-swatch-variant - include_in_menu: false + include_in_menu: true human_friendly_id: - center_wavelength_nm__value order_by: @@ -1497,7 +1498,7 @@ nodes: description: What a transponder or coherent pluggable can do. Reach and required OSNR are data, not assumptions. label: Optical mode icon: mdi:waveform - include_in_menu: false + include_in_menu: true human_friendly_id: - name__value order_by: @@ -1636,7 +1637,7 @@ nodes: description: What a customer hands over, and the container it maps into. label: Client signal icon: mdi:import - include_in_menu: false + include_in_menu: true human_friendly_id: - name__value order_by: @@ -1764,7 +1765,7 @@ nodes: description: ODU or SDH virtual container. The digital adaptation layer between a client and a wavelength. label: Container icon: mdi:package-variant-closed - include_in_menu: false + include_in_menu: true human_friendly_id: - name__value order_by: @@ -1900,7 +1901,8 @@ nodes: description: IP router. Light terminates here, so it contributes no insertion loss. label: Router icon: mdi:router - include_in_menu: false + include_in_menu: true + menu_placement: OtnGenericDevice inherit_from: - OtnGenericDevice @@ -1909,7 +1911,8 @@ nodes: description: Client to DWDM line adaptation. label: Transponder icon: mdi:transit-connection-variant - include_in_menu: false + include_in_menu: true + menu_placement: OtnGenericDevice inherit_from: - OtnGenericDevice - OtnOpticalElement @@ -1919,7 +1922,8 @@ nodes: description: Reconfigurable optical add/drop multiplexer. label: ROADM icon: mdi:call-split - include_in_menu: false + include_in_menu: true + menu_placement: OtnGenericDevice inherit_from: - OtnGenericDevice - OtnOpticalElement @@ -1948,7 +1952,8 @@ nodes: description: Inline, booster or pre-amplifier. label: Amplifier icon: mdi:amplifier - include_in_menu: false + include_in_menu: true + menu_placement: OtnGenericDevice inherit_from: - OtnGenericDevice - OtnOpticalElement @@ -2033,7 +2038,8 @@ nodes: description: Passive multiplexer. A dense AWG on the core, a coarse thin-film filter on a tail. label: Mux/demux icon: mdi:call-merge - include_in_menu: false + include_in_menu: true + menu_placement: OtnGenericDevice inherit_from: - OtnGenericDevice - OtnOpticalElement @@ -2053,7 +2059,8 @@ nodes: description: Optical distribution frame. Carries connector loss. label: Patch panel icon: mdi:view-grid-outline - include_in_menu: false + include_in_menu: true + menu_placement: OtnGenericDevice inherit_from: - OtnGenericDevice - OtnOpticalElement @@ -2063,7 +2070,8 @@ nodes: description: Pump laser injecting Raman gain into one fiber span. label: Raman pump icon: mdi:laser-pointer - include_in_menu: false + include_in_menu: true + menu_placement: OtnGenericDevice # Two generics side by side: a kind that is both a racked device and something # light passes through has to say so twice. No attribute stores which direction # the pump amplifies; injection_end and propagation are what compute it. @@ -2136,7 +2144,8 @@ nodes: description: O-E-O device. Terminates one wavelength, originates the next, as a regenerator or ODU cross-connect. label: ODU switch icon: mdi:swap-horizontal-variant - include_in_menu: false + include_in_menu: true + menu_placement: OtnGenericDevice # OtnOpticalElement is here because OtnPathHop.element peers that generic. The # inherited insertion_loss_mdb applies to the incoming segment only: this device # terminates the light, so the outgoing segment starts at a transmitter. @@ -2192,7 +2201,8 @@ nodes: description: A pad. One fixed amount of loss, patched into a link that arrives too hot. label: Fixed attenuator icon: mdi:filter-outline - include_in_menu: false + include_in_menu: true + menu_placement: OtnGenericDevice inherit_from: - OtnGenericDevice - OtnOpticalElement @@ -2222,7 +2232,8 @@ nodes: description: A VOA. The same loss, dialled rather than fixed, within the range the hardware has. label: Variable attenuator icon: mdi:tune-variant - include_in_menu: false + include_in_menu: true + menu_placement: OtnGenericDevice inherit_from: - OtnGenericDevice - OtnOpticalElement @@ -2274,7 +2285,7 @@ nodes: description: One provisioned wavelength. Occupies its channel on every section it crosses. label: Optical carrier icon: mdi:waves - include_in_menu: false + include_in_menu: true human_friendly_id: - name__value order_by: @@ -2382,7 +2393,7 @@ nodes: description: Customer intent. Two endpoints, a rate, a profile and an optional latency budget. label: Service icon: mdi:file-document-outline - include_in_menu: false + include_in_menu: true inherit_from: - CoreArtifactTarget human_friendly_id: From 0787a6bd21c6c7a028df22ff99a0cde65d72fc5f Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 13:10:09 +0200 Subject: [PATCH 15/31] docs(otn): stop the facility comment claiming every kind is hidden --- extensions/otn/otn.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index f43ac000..223a473a 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -2752,7 +2752,7 @@ nodes: description: A supercomputing facility hosted at a PoP. label: Facility icon: ri:cpu-line - # Reached through the site it sits on, like every other kind here. + # Reached through the site it sits on. include_in_menu: false human_friendly_id: - name__value From a16073226659b8c215d03d72259dfba79573e857 Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 13:26:14 +0200 Subject: [PATCH 16/31] feat(otn): add OTN sample objects --- objects/extensions/otn/otn.yml | 276 +++++++++++++++++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 objects/extensions/otn/otn.yml diff --git a/objects/extensions/otn/otn.yml b/objects/extensions/otn/otn.yml new file mode 100644 index 00000000..c7f984a2 --- /dev/null +++ b/objects/extensions/otn/otn.yml @@ -0,0 +1,276 @@ +# --------------------------------------------------------------------------- +# Mock data for extensions/otn. +# Depends on: base (OrganizationProvider: Lumen), extensions/location_minimal +# (LocationSite: NYC1, SJC1). +# +# One facility, one span, one wavelength and one service end to end. Enough to +# walk the model from a service down to the ports it lands on, not a +# representative network. +# +# References use each kind's human_friendly_id: a device by name, a port by +# device plus name. OtnFrequencyGrid is referenced with a nested kind/data +# block instead, because its hfid is a Number. +# --------------------------------------------------------------------------- +--- +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnFacility + data: + # Facility names are lower case with hyphens. + - name: nyc1-gpu-cluster + description: GPU training cluster handing 400G client traffic to the NYC1 PoP. + site: NYC1 + +--- +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnFiberType + data: + - name: smf-g652d + description: ITU-T G.652.D standard single mode fiber. + attenuation_coefficient_mdb_per_km: 190 + dispersion_fs_per_nm_km: 17000 + +--- +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnFrequencyGrid + data: + # ITU-T G.694.1 100 GHz grid, channel 21 at 192.1 THz. + - channel_number: 21 + center_frequency_mhz: 192100000 + +--- +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnTransceiverType + data: + - part_number: QDD-400G-DR4 + vendor: Cisco + description: 400G QSFP-DD grey client optic, 500 m over parallel single mode fiber. + form_factor: QSFP-DD + tunable: false + +--- +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnTransceiver + data: + # The port claims the transceiver, further down, rather than the other way + # round: `port` peers with the OtnOpticalPort generic, which has no hfid. + - serial: TRX-NYC1-0001 + status: in_service + type: QDD-400G-DR4 + +--- +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnTransponder + data: + - name: nyc1-txp01 + status: active + role: edge + element_class: transponder + vendor: Cisco + model: NCS 1004 + site: NYC1 + ports: + - kind: OtnClientPort + data: + name: client-1/0/0 + role: client + oper_state: up + connector_type: MPO-12 + polish: APC + transceiver: TRX-NYC1-0001 + - kind: OtnLinePort + data: + name: line-1/0/0 + role: line + oper_state: up + connector_type: LC + polish: UPC + center_frequency_mhz: 192100000 + tx_power_mdbm: 1000 + rx_sensitivity_mdbm: -25000 + + # The far end of the wavelength. OtnService.endpoint_z is mandatory, so the + # SJC1 end of the span needs a device of its own. + - name: sjc1-txp01 + status: active + role: edge + element_class: transponder + vendor: Cisco + model: NCS 1004 + site: SJC1 + ports: + - kind: OtnLinePort + data: + name: line-1/0/0 + role: line + oper_state: up + connector_type: LC + polish: UPC + center_frequency_mhz: 192100000 + tx_power_mdbm: 1000 + rx_sensitivity_mdbm: -25000 + +--- +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnRoadm + data: + - name: nyc1-rdm01 + status: active + role: core + element_class: roadm + vendor: Cisco + model: NCS 2006 + insertion_loss_mdb: 5500 + site: NYC1 + ports: + - kind: OtnRoadmAddDropPort + data: + name: add-drop-1/1 + role: add_drop + oper_state: up + connector_type: LC + polish: UPC + # The transponder line port patches into this add/drop port. + connected_to: [nyc1-txp01, line-1/0/0] + - kind: OtnRoadmDegreePort + data: + name: degree-1 + role: degree + oper_state: up + connector_type: LC + polish: UPC + +--- +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnMuxDemux + data: + - name: nyc1-mux01 + status: active + role: passive + element_class: mux_demux + vendor: Cisco + model: NCS1K-MD-64-C + insertion_loss_mdb: 3500 + site: NYC1 + ports: + - kind: OtnMuxClientPort + data: + name: mux-client-1 + role: client + oper_state: up + connector_type: LC + polish: UPC + dwdm_channel: + kind: OtnFrequencyGrid + data: + channel_number: 21 + center_frequency_mhz: 192100000 + - kind: OtnMuxLinePort + data: + name: mux-line-1 + role: line + oper_state: up + connector_type: LC + polish: UPC + +--- +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnFiberSpan + data: + # A single 82 km span stands in for the NYC1 to SJC1 route, which in a real + # network is many spans with amplifiers between them. + - name: nyc1-sjc1-span-1 + description: Leased dark fiber between NYC1 and SJC1. + element_class: fiber_span + length_m: 82000 + insertion_loss_mdb: 15580 + splice_count: 20 + fiber_type: smf-g652d + site_a: NYC1 + site_b: SJC1 + terminating_ports: + - [nyc1-rdm01, degree-1] + +--- +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnOpticalCarrier + data: + - name: nyc1-sjc1-ch21 + description: 400G wavelength on channel 21 between NYC1 and SJC1. + status: active + channel: + kind: OtnFrequencyGrid + data: + channel_number: 21 + center_frequency_mhz: 192100000 + line_ports: + - [nyc1-txp01, line-1/0/0] + - [sjc1-txp01, line-1/0/0] + +--- +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnService + data: + - name: SVC-NYC1-SJC1-0001 + description: 400G wavelength sold to Lumen between NYC1 and SJC1. + rate_gbps: 400 + sla: gold + status: active + service_profile: ai-training-dci + max_latency_ns: 500000 + endpoint_a: nyc1-txp01 + endpoint_z: sjc1-txp01 + customer: Lumen + optical_path: + kind: OtnOpticalPath + data: + - name: nyc1-sjc1-path-1 + description: Single segment carrying the wavelength from NYC1 to SJC1. + segment_sequence: 1 + carrier: nyc1-sjc1-ch21 + hops: + kind: OtnPathHop + data: + # `element` peers with the OtnOpticalElement generic, which has + # no hfid, so each hop names the concrete kind and upserts + # against the object created above by its mandatory fields. + - name: nyc1-sjc1-hop-1 + sequence: 1 + element: + kind: OtnRoadm + data: + name: nyc1-rdm01 + role: core + element_class: roadm + - name: nyc1-sjc1-hop-2 + sequence: 2 + element: + kind: OtnFiberSpan + data: + name: nyc1-sjc1-span-1 + element_class: fiber_span + length_m: 82000 + fiber_type: smf-g652d + site_a: NYC1 + site_b: SJC1 From acb98bdd59777bbd63e07e0222321305b806a6e5 Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 13:41:51 +0200 Subject: [PATCH 17/31] docs: publish the OTN reference page and its scope --- .metadata.yml | 24 + docs/docs/home.mdx | 1 + docs/docs/reference/otn.mdx | 3530 +++++++++++++++++++++++++++++++++++ 3 files changed, 3555 insertions(+) create mode 100644 docs/docs/reference/otn.mdx diff --git a/.metadata.yml b/.metadata.yml index 96884bfc..b216ecb1 100644 --- a/.metadata.yml +++ b/.metadata.yml @@ -228,6 +228,30 @@ extensions/otn: description: | Optical transport network schemas covering the physical plant, the wavelength catalog, optical devices and their ports, pluggable optics, carriers and end-to-end services. name: OTN + not_covered: + - Cabling between optical devices. No OTN port is a DcimEndpoint, so extensions/cable + cannot terminate on optical gear. Fiber is modelled as OtnFiberSpan and OtnConduit + instead. + - Validation of what a schema cannot express, such as which port kind may hold + a pluggable, or whether a mux client port binds exactly one channel. This extension + ships no checks; the schema comments mark each such rule. + - Rack elevations, device types and platforms for optical gear. Model the physical + asset as a DcimPhysicalDevice and link it with OtnGenericDevice.dcim_device. + - Optical performance monitoring. No kind records optical power, OSNR, pre-FEC + BER or any other reading. That data changes continuously and belongs in the + network management system or a time series store, not in a source of truth. + - Optical path budgets. OtnOpticalPath records which elements the light crosses + and in what order, not the loss, OSNR margin or latency of that route. Compute + those in a planning tool such as GNPy. + use_cases: + - Recording the ITU-T G.694.1 dense grid and G.694.2 coarse plan as separate kinds, + so a carrier pointed at the wrong plan is refused when written. + - Tracking pluggable optics as inventory, by part number and by serial, and which + port each unit is fitted in. + - Modelling the outside plant, including fiber types, conduits, spans and the + optical multiplex sections built over them. + - Provisioning a wavelength end to end, as a service with an optical path and + its hops through the elements the light crosses. extensions/patch_panel: dependencies: - base diff --git a/docs/docs/home.mdx b/docs/docs/home.mdx index e180525e..892e3a13 100644 --- a/docs/docs/home.mdx +++ b/docs/docs/home.mdx @@ -123,6 +123,7 @@ This list provides an overview of the schemas available in this repository. Each | **[Location Minimal](./reference/location_minimal.mdx)** | This schema extension provides a self-contained Region -> Country -> Metro -> Site hierarchy for storing location data, with the Site carrying facility, physical address, timezone and status. Its Site node is the same as the one in extensions/location_site, so the two can be loaded together. A location name is unique across every tier, so a single-country deployment should enter the hierarchy at Country, with Region as the national node, rather than repeating a country under several regions. | | **[Location Site](./reference/location_site.mdx)** | This schema extension introduces a Site node with facility, physical address, timezone and status, for deployments that want a flat list of sites without a hierarchy. It is the same Site node as in extensions/location_minimal, which adds Region, Country and Metro tiers above it. | | **[MLAG](./reference/mlag.mdx)** | This schema extension contains the foundations to capture Multi-Chassis Link Aggregation Groups (MLAG). It comes on top of the LAG extension. In this implementation, a MLAG interface is essentially a LAG interface but linked to a MLAG domain (instead of a device). The MLAG domain regroups devices together (usually 2) and is built over LAG interfaces used as peer-link between the devices. MLAG interfaces defined at the MLAG domain level are then spread across all devices in the domain. This is a deliberately minimal implementation of MLAG, meant to blend with models you already have. For example, in a data center fabric you might already have a LeafGroup or similar concept: have it inherit from GenericMlagDomain and add the relationships and attributes you need. Not covered yet: the layer 3 overlay for MLAG interfaces (loopback, peer address ...) and surfacing MLAG interfaces on each device in the domain. | +| **[OTN](./reference/otn.mdx)** | Optical transport network schemas covering the physical plant, the wavelength catalog, optical devices and their ports, pluggable optics, carriers and end-to-end services. | | **[Patch Panel](./reference/patch_panel.mdx)** | This schema extension allows you to capture patch panel related information like rear and front interfaces and the mapping between them. You can insert the patch panel into a rack and leverage the device type model. Cassettes and other inserts are tracked as regular device modules in module bays, through extensions/device_module. The front and rear interfaces accept all sorts of connectors, so you can plug cables, circuits and cross-connects into them. | | **[Physical Disk](./reference/physical_disk.mdx)** | Simple schema allowing you to capture physical disk information for inventory and lifecycle management. This extension works with any kind of device: apply the DcimDeviceWithPhysicalDisks generic to a model to enable disk tracking. You might also link disks to a location, for instance to capture spares. | | **[PSU Module](./reference/device_module_psu.mdx)** | This schema extension adds a PSU (Power Supply Unit) flavour on top of the generic Module and Module Type from extensions/device_module, so you can track power supplies installed in a device's module bays with PSU-specific attributes such as wattage and hot-swap capability. | diff --git a/docs/docs/reference/otn.mdx b/docs/docs/reference/otn.mdx new file mode 100644 index 00000000..0aac424d --- /dev/null +++ b/docs/docs/reference/otn.mdx @@ -0,0 +1,3530 @@ +--- +title: OTN +--- + +Optical transport network schemas covering the physical plant, the wavelength catalog, optical devices and their ports, pluggable optics, carriers and end-to-end services. + +## Details + +- **Dependencies:** + - [base](dcim) + - [extensions/location_site](location_site) + +## Use cases + +This extension covers: +- Recording the ITU-T G.694.1 dense grid and G.694.2 coarse plan as separate kinds, so a carrier pointed at the wrong plan is refused when written. +- Tracking pluggable optics as inventory, by part number and by serial, and which port each unit is fitted in. +- Modelling the outside plant, including fiber types, conduits, spans and the optical multiplex sections built over them. +- Provisioning a wavelength end to end, as a service with an optical path and its hops through the elements the light crosses. + +Out of scope, and what to reach for instead: +- Cabling between optical devices. No OTN port is a DcimEndpoint, so extensions/cable cannot terminate on optical gear. Fiber is modelled as OtnFiberSpan and OtnConduit instead. +- Validation of what a schema cannot express, such as which port kind may hold a pluggable, or whether a mux client port binds exactly one channel. This extension ships no checks; the schema comments mark each such rule. +- Rack elevations, device types and platforms for optical gear. Model the physical asset as a DcimPhysicalDevice and link it with OtnGenericDevice.dcim_device. +- Optical performance monitoring. No kind records optical power, OSNR, pre-FEC BER or any other reading. That data changes continuously and belongs in the network management system or a time series store, not in a source of truth. +- Optical path budgets. OtnOpticalPath records which elements the light crosses and in what order, not the loss, OSNR margin or latency of that route. Compute those in a planning tool such as GNPy. + +## Nodes + +### RouterPort + +- **Label:** Router port +- **Description:** Grey optics on an IP router. Not on the C-band grid. +- **Namespace:** Otn +- **Icon:** mdi:ethernet +- **Inherit From:** OtnGenericPort, OtnOpticalPort + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| connector_type | | Dropdown | True | | LC, SC, MPO-12, MPO-16, FC, E2000, MU, CS, splice | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| transceiver | OtnTransceiver | True | one | Attribute | + +### ClientPort + +- **Label:** Client port +- **Description:** Transponder client side. Where grey light arrives. +- **Namespace:** Otn +- **Icon:** mdi:lan-connect +- **Inherit From:** OtnGenericPort, OtnOpticalPort + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| connector_type | | Dropdown | True | | LC, SC, MPO-12, MPO-16, FC, E2000, MU, CS, splice | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| transceiver | OtnTransceiver | True | one | Attribute | + +### LinePort + +- **Label:** Line port +- **Description:** Transponder DWDM line side. Where coloured light leaves. +- **Namespace:** Otn +- **Icon:** mdi:transit-connection +- **Inherit From:** OtnGenericPort, OtnOpticalPort + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| connector_type | | Dropdown | True | | LC, SC, MPO-12, MPO-16, FC, E2000, MU, CS, splice | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| carrier | OtnOpticalCarrier | True | one | Attribute | +| transceiver | OtnTransceiver | True | one | Attribute | + +### RoadmAddDropPort + +- **Label:** ROADM add/drop port +- **Description:** Local add and drop on a ROADM. +- **Namespace:** Otn +- **Icon:** mdi:call-split +- **Inherit From:** OtnGenericPort, OtnOpticalPort + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| connector_type | | Dropdown | True | | LC, SC, MPO-12, MPO-16, FC, E2000, MU, CS, splice | + +### RoadmDegreePort + +- **Label:** ROADM degree port +- **Description:** Line-facing degree on a ROADM. One per direction. +- **Namespace:** Otn +- **Icon:** mdi:compass-outline +- **Inherit From:** OtnGenericPort, OtnOpticalPort + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| connector_type | | Dropdown | True | | LC, SC, MPO-12, MPO-16, FC, E2000, MU, CS, splice | + +### AmplifierPort + +- **Label:** Amplifier port +- **Description:** Amplifier input or output. +- **Namespace:** Otn +- **Icon:** mdi:amplifier +- **Inherit From:** OtnGenericPort, OtnOpticalPort + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| connector_type | | Dropdown | True | | LC, SC, MPO-12, MPO-16, FC, E2000, MU, CS, splice | + +### MuxClientPort + +- **Label:** Mux client port +- **Description:** One channel of a multiplexer. The side facing the transponder or router that lights it. +- **Namespace:** Otn +- **Icon:** mdi:import +- **Inherit From:** OtnGenericPort, OtnOpticalPort + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| dwdm_channel | OtnFrequencyGrid | True | one | Attribute | +| cwdm_channel | OtnCwdmChannel | True | one | Attribute | + +### MuxLinePort + +- **Label:** Mux line port +- **Description:** The common side of a multiplexer, where the whole band leaves. +- **Namespace:** Otn +- **Icon:** mdi:export +- **Inherit From:** OtnGenericPort, OtnOpticalPort + +### TributaryPort + +- **Label:** Tributary port +- **Description:** E1 or T1 G.703 electrical tributary. The only copper port here. +- **Namespace:** Otn +- **Icon:** mdi:cable-data +- **Inherit From:** OtnGenericPort, OtnCopperPort + +### TransceiverType + +- **Label:** Transceiver type +- **Description:** A pluggable optic part number and what it can be made to do. +- **Namespace:** Otn +- **Icon:** mdi:chip +- **Human Friendly ID:** part_number__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| part_number | Vendor part number, such as QDD-400G-ZRP-S. | Text | False | | | +| vendor | | Text | True | | | +| description | | Text | True | | | +| form_factor | The cage this part fits. It decides which ports can hold it. | Dropdown | False | | QSFP-DD, QSFP28, OSFP, CFP2-DCO, CFP2-ACO, SFP28, SFP+ | +| tunable | Whether the laser can be tuned across the grid or is fixed at one wavelength. | Boolean | False | False | | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| supported_modes | OtnOpticalMode | True | many | Attribute | + +### Transceiver + +- **Label:** Transceiver +- **Description:** One physical pluggable optic, fitted in a port or sitting on a shelf. +- **Namespace:** Otn +- **Icon:** mdi:memory +- **Human Friendly ID:** serial__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| serial | Vendor serial number. The only thing that identifies this unit. | Text | False | | | +| status | | Dropdown | False | spare | in_service, spare, rma, decommissioned | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| type | OtnTransceiverType | False | one | Attribute | +| port | OtnOpticalPort | True | one | Attribute | + +### FiberType + +- **Label:** Fiber type +- **Description:** Single-mode fiber family. Attenuation, dispersion and group index at 1550 nm. +- **Namespace:** Otn +- **Icon:** mdi:cable-data +- **Human Friendly ID:** name__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| name | ITU-T designation, such as G.652.D. | Text | False | | | +| description | | Text | True | | | +| attenuation_coefficient_mdb_per_km | Attenuation at 1550 nm in millidecibels per kilometre. 0.20 dB/km is 200. Wrong for CWDM. | Number | False | | | +| attenuation_coefficient_display | Attenuation in dB/km. Read-only, derived from attenuation_coefficient_mdb_per_km. | Text | True | | | +| dispersion_fs_per_nm_km | Chromatic dispersion at 1550 nm in femtoseconds per nm per km. 17 ps/nm/km is 17000. | Number | False | | | +| dispersion_display | Dispersion in ps/nm/km. Read-only, derived from dispersion_fs_per_nm_km. | Text | True | | | +| group_index_milli | Group index at 1550 nm in milli-units. 1.468 is 1468. Sets propagation delay. | Number | False | 1468 | | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| spans | OtnFiberSpan | True | many | Attribute | + +### Conduit + +- **Label:** Conduit +- **Description:** Shared-risk link group. Two routes through one conduit are not diverse. +- **Namespace:** Otn +- **Icon:** mdi:pipe +- **Human Friendly ID:** name__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| name | | Text | False | | | +| owner | Who owns the trench. Often not the network operator. | Text | True | | | +| description | | Text | True | | | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| spans | OtnFiberSpan | True | many | Attribute | + +### FiberSpan + +- **Label:** Fiber span +- **Description:** One amplifier-to-amplifier fiber section. Lossy, but not racked and not a device. +- **Namespace:** Otn +- **Icon:** mdi:vector-line +- **Human Friendly ID:** name__value +- **Inherit From:** OtnOpticalElement + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| name | | Text | False | | | +| description | | Text | True | | | +| oms_sequence | Position of this span within its optical multiplex section, counting from the A end. | Number | True | | | +| length_m | Route length in metres, not straight-line distance. 500 km is the practical ceiling. | Number | False | | | +| length_display | Route length in km. Read-only, derived from length_m. | Text | True | | | +| splice_count | Fusion splices along the span. Roughly one per drum of cable. | Number | False | 0 | | +| splice_loss_mdb | Loss per splice in millidecibels. 0.05 dB is 50. | Number | False | 50 | | +| connector_count | Mated connector pairs on the span, normally one at each end. | Number | False | 2 | | +| connector_loss_mdb | Loss per mated connector pair in millidecibels. 0.3 dB is 300. | Number | False | 300 | | +| aging_margin_mdb | Reserved margin for repairs and ageing, in millidecibels. 1.5 dB is 1500. | Number | False | 1500 | | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| fiber_type | OtnFiberType | False | one | Attribute | +| site_a | LocationSite | False | one | Attribute | +| site_b | LocationSite | False | one | Attribute | +| conduit | OtnConduit | True | one | Attribute | +| oms | OtnOpticalMultiplexSection | True | one | Attribute | +| raman_pumps | OtnRamanPump | True | many | Attribute | +| terminating_ports | OtnGenericPort | True | many | Attribute | + +### OpticalMultiplexSection + +- **Label:** Optical multiplex section +- **Description:** ROADM to ROADM. Groups the ordered spans and inline amplifiers between two degrees. +- **Namespace:** Otn +- **Icon:** mdi:ray-start-end +- **Human Friendly ID:** name__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| name | | Text | False | | | +| description | | Text | True | | | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| roadm_a | OtnRoadm | False | one | Attribute | +| roadm_b | OtnRoadm | False | one | Attribute | +| spans | OtnFiberSpan | True | many | Attribute | +| amplifiers_a2b | OtnAmplifier | True | many | Attribute | +| amplifiers_b2a | OtnAmplifier | True | many | Attribute | + +### FrequencyGrid + +- **Label:** Frequency grid channel +- **Description:** One ITU-T G.694.1 channel. 50 GHz fixed grid, 191.35 to 196.10 THz. +- **Namespace:** Otn +- **Icon:** mdi:sine-wave +- **Human Friendly ID:** channel_number__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| channel_number | ITU channel number, 1 to 96. Channel 1 is 191.35 THz. | Number | False | | | +| center_frequency_mhz | Centre frequency in MHz. Channel n is 191350000 + (n - 1) x 50000. | Number | False | | | +| center_frequency_display | Centre frequency in THz. Byte-identical to the optical-port rendering. | Text | True | | | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| carriers | OtnOpticalCarrier | True | many | Attribute | + +### CwdmChannel + +- **Label:** CWDM wavelength +- **Description:** One ITU-T G.694.2 coarse wavelength. 20 nm spacing, 1271 to 1611 nm. +- **Namespace:** Otn +- **Icon:** mdi:palette-swatch-variant +- **Human Friendly ID:** center_wavelength_nm__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| center_wavelength_nm | Nominal central wavelength in nm. Wavelength n is 1271 + (n - 1) x 20. | Number | False | | | +| band | ITU band. Only the two C-band wavelengths sit in the erbium window. | Dropdown | False | | o, e, s, c, l | + +### OpticalMode + +- **Label:** Optical mode +- **Description:** What a transponder or coherent pluggable can do. Reach and required OSNR are data, not assumptions. +- **Namespace:** Otn +- **Icon:** mdi:waveform +- **Human Friendly ID:** name__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| name | | Text | False | | | +| mode_class | Two values, and the pluggable-reach report filters on it. | Dropdown | False | transponder | transponder, pluggable | +| modulation | | Text | False | | | +| description | | Text | True | | | +| line_rate_gbps | Client-side capacity in Gbps. Already a whole number in its natural unit. | Number | False | | | +| baud_mbaud | Symbol rate in megabaud. 59.84 GBd is 59840. Sets the spectral width. | Number | False | | | +| required_osnr_mdb | OSNR needed at the receiver, in millidecibels. 26 dB is 26000. | Number | False | | | +| required_osnr_display | Required OSNR in dB. Read-only, derived from required_osnr_mdb. | Text | True | | | +| cd_tolerance_fs_per_nm | Chromatic dispersion the receiver compensates, in fs/nm. 100000 ps/nm is 100000000. | Number | False | | | +| nominal_reach_m | Vendor-quoted reach in metres. A starting point for the budget, not a guarantee. | Number | False | | | +| nominal_reach_display | Nominal reach in km. Read-only, derived from nominal_reach_m. | Text | True | | | +| fec_type | | Text | False | SD-FEC | | +| fec_latency_ns | Encoder and decoder latency in nanoseconds. Small next to propagation, but not zero. | Number | False | 0 | | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| carriers | OtnOpticalCarrier | True | many | Attribute | + +### ClientSignal + +- **Label:** Client signal +- **Description:** What a customer hands over, and the container it maps into. +- **Namespace:** Otn +- **Icon:** mdi:import +- **Human Friendly ID:** name__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| name | | Text | False | | | +| alias | The SONET name where one exists. STM-16 is OC-48. | Text | True | | | +| layer | Grouping the catalog by layer is the first thing a reader does. | Dropdown | False | | ethernet, sdh, pdh, fibre_channel, infiniband | +| auto_selectable | May the rate rule pick this signal when a service states none. | Boolean | False | False | | +| description | | Text | True | | | +| bit_rate_kbps | Nominal line rate in kbps. E1 is 2048, 100GBASE-LR4 is 103100000. | Number | False | | | +| bit_rate_display | Bit rate in Mbps below one gigabit, Gbps at or above it. | Text | True | | | +| default_container_type | The first step of the mapping chain, not the last. E1 maps into VC-12, not ODU1. | Text | False | | | +| default_mapping | ITU-T G.709 mapping procedure. Generic, bit-synchronous or asynchronous. | Text | False | | | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| containers | OtnContainer | True | many | Attribute | +| services | OtnService | True | many | Attribute | + +### Container + +- **Label:** Container +- **Description:** ODU or SDH virtual container. The digital adaptation layer between a client and a wavelength. +- **Namespace:** Otn +- **Icon:** mdi:package-variant-closed +- **Human Friendly ID:** name__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| name | | Text | False | | | +| odu_type | | Text | False | | | +| mapping_mode | | Text | False | GMP | | +| description | | Text | True | | | +| segment_sequence | Which segment of its circuit this container rides. 1 for a circuit that spans one wavelength. | Number | False | 1 | | +| tributary_slots | Slots this container occupies in its parent. An ODU2 in an ODU4 takes 8. | Number | False | 0 | | +| tributary_slot_capacity | Slots this container offers to its children. An ODU4 offers 80. | Number | False | 0 | | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| carrier | OtnOpticalCarrier | True | one | Attribute | +| client_signal | OtnClientSignal | True | one | Attribute | +| parent_container | OtnContainer | True | one | Attribute | +| service | OtnService | True | one | Attribute | +| child_containers | OtnContainer | True | many | Attribute | + +### Router + +- **Label:** Router +- **Description:** IP router. Light terminates here, so it contributes no insertion loss. +- **Namespace:** Otn +- **Icon:** mdi:router +- **Inherit From:** OtnGenericDevice + +### Transponder + +- **Label:** Transponder +- **Description:** Client to DWDM line adaptation. +- **Namespace:** Otn +- **Icon:** mdi:transit-connection-variant +- **Inherit From:** OtnGenericDevice, OtnOpticalElement + +### Roadm + +- **Label:** ROADM +- **Description:** Reconfigurable optical add/drop multiplexer. +- **Namespace:** Otn +- **Icon:** mdi:call-split +- **Inherit From:** OtnGenericDevice, OtnOpticalElement + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| sections_a | OtnOpticalMultiplexSection | True | many | Attribute | +| sections_b | OtnOpticalMultiplexSection | True | many | Attribute | + +### Amplifier + +- **Label:** Amplifier +- **Description:** Inline, booster or pre-amplifier. +- **Namespace:** Otn +- **Icon:** mdi:amplifier +- **Inherit From:** OtnGenericDevice, OtnOpticalElement + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| noise_figure_mdb | Noise figure in millidecibels. 4.0 dB is 4000. Sets the ASE this stage adds. | Number | False | 4000 | | +| noise_figure_display | Noise figure in dB. Read-only, derived from noise_figure_mdb. | Text | True | | | +| gain_mdb | Gain in millidecibels. 22.0 dB is 22000. Must cover the loss ahead of the input. | Number | False | 22000 | | +| gain_display | Gain in dB. Read-only, derived from gain_mdb. | Text | True | | | +| oms_sequence | Position in this amplifier's own chain, counting along the direction it amplifies. | Number | False | | | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| oms_a2b | OtnOpticalMultiplexSection | True | one | Attribute | +| oms_b2a | OtnOpticalMultiplexSection | True | one | Attribute | + +### MuxDemux + +- **Label:** Mux/demux +- **Description:** Passive multiplexer. A dense AWG on the core, a coarse thin-film filter on a tail. +- **Namespace:** Otn +- **Icon:** mdi:call-merge +- **Inherit From:** OtnGenericDevice, OtnOpticalElement + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| cwdm_channels | OtnCwdmChannel | True | many | Attribute | + +### PatchPanel + +- **Label:** Patch panel +- **Description:** Optical distribution frame. Carries connector loss. +- **Namespace:** Otn +- **Icon:** mdi:view-grid-outline +- **Inherit From:** OtnGenericDevice, OtnOpticalElement + +### RamanPump + +- **Label:** Raman pump +- **Description:** Pump laser injecting Raman gain into one fiber span. +- **Namespace:** Otn +- **Icon:** mdi:laser-pointer +- **Inherit From:** OtnGenericDevice, OtnOpticalElement + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| on_off_gain_mdb | Raman on-off gain in millidecibels. 10.0 dB is 10000. | Number | False | 10000 | | +| on_off_gain_display | On-off gain in dB. Read-only, derived from on_off_gain_mdb. | Text | True | | | +| injection_end | Which end of its span the pump is injected at, named against the span's own site_a and site_b. | Dropdown | False | site_b | site_a, site_b | +| propagation | Whether the pump fires against the signal or with it. | Dropdown | False | counter | counter, co | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| span | OtnFiberSpan | False | one | Attribute | + +### OduSwitch + +- **Label:** ODU switch +- **Description:** O-E-O device. Terminates one wavelength, originates the next, as a regenerator or ODU cross-connect. +- **Namespace:** Otn +- **Icon:** mdi:swap-horizontal-variant +- **Inherit From:** OtnGenericDevice, OtnOpticalElement + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| switching_mode | Whether the device carries the whole payload through or demultiplexes and regroups containers. | Dropdown | False | regenerator | regenerator, cross_connect | +| framing_latency_ns | Delay this device adds in nanoseconds, from framing and from the electrical crossing. | Number | False | 0 | | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| carriers | OtnOpticalCarrier | True | many | Attribute | + +### FixedAttenuator + +- **Label:** Fixed attenuator +- **Description:** A pad. One fixed amount of loss, patched into a link that arrives too hot. +- **Namespace:** Otn +- **Icon:** mdi:filter-outline +- **Inherit From:** OtnGenericDevice, OtnOpticalElement + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| attenuation_mdb | Attenuation the pad is set to, in millidecibels. 5.0 dB is 5000. | Number | False | 0 | | +| attenuation_display | Attenuation in dB. Read-only, derived from attenuation_mdb. | Text | True | | | + +### VariableAttenuator + +- **Label:** Variable attenuator +- **Description:** A VOA. The same loss, dialled rather than fixed, within the range the hardware has. +- **Namespace:** Otn +- **Icon:** mdi:tune-variant +- **Inherit From:** OtnGenericDevice, OtnOpticalElement + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| attenuation_mdb | Attenuation the pad is set to, in millidecibels. 5.0 dB is 5000. | Number | False | 0 | | +| attenuation_display | Attenuation in dB. Read-only, derived from attenuation_mdb. | Text | True | | | +| max_attenuation_mdb | Largest attenuation this device can be dialled to, in millidecibels. | Number | False | | | +| max_attenuation_display | Maximum attenuation in dB. Read-only, derived from max_attenuation_mdb. | Text | True | | | + +### OpticalCarrier + +- **Label:** Optical carrier +- **Description:** One provisioned wavelength. Occupies its channel on every section it crosses. +- **Namespace:** Otn +- **Icon:** mdi:waves +- **Human Friendly ID:** name__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| name | | Text | False | | | +| description | | Text | True | | | +| status | | Dropdown | False | active | planned, active, decommissioned | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| channel | OtnFrequencyGrid | False | one | Attribute | +| optical_mode | OtnOpticalMode | True | one | Attribute | +| optical_path | OtnOpticalPath | True | many | Attribute | +| sections | OtnOpticalMultiplexSection | True | many | Attribute | +| containers | OtnContainer | True | many | Attribute | +| odu_switches | OtnOduSwitch | True | many | Attribute | +| line_ports | OtnLinePort | True | many | Attribute | + +### Service + +- **Label:** Service +- **Description:** Customer intent. Two endpoints, a rate, a profile and an optional latency budget. +- **Namespace:** Otn +- **Icon:** mdi:file-document-outline +- **Human Friendly ID:** name__value +- **Inherit From:** CoreArtifactTarget + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| name | | Text | False | | | +| description | | Text | True | | | +| rate_gbps | Requested client capacity in Gbps. Already whole in its natural unit. | Number | False | | | +| sla | | Dropdown | False | silver | gold, silver, bronze, best_effort | +| status | | Dropdown | False | planned | planned, provisioning, active, rejected, decommissioned | +| service_profile | | Dropdown | False | ip-transit | ai-training-dci, ai-inference, hpc-research, ip-transit, legacy-sdh | +| max_latency_ns | One-way latency budget in nanoseconds. Null on the profiles that have none. | Number | True | | | +| max_latency_display | Latency budget in microseconds. Read-only, derived from max_latency_ns. | Text | True | | | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| endpoint_a | OtnGenericDevice | False | one | Attribute | +| endpoint_z | OtnGenericDevice | False | one | Attribute | +| optical_path | OtnOpticalPath | True | many | Component | +| client_signal | OtnClientSignal | True | one | Attribute | +| diversity_group | OtnDiversityGroup | True | one | Attribute | +| containers | OtnContainer | True | many | Attribute | +| customer | OrganizationGeneric | False | one | Attribute | + +### DiversityGroup + +- **Label:** Diversity group +- **Description:** A declared diversity requirement. The services in one group must route over disjoint conduits. +- **Namespace:** Otn +- **Icon:** mdi:call-split +- **Human Friendly ID:** name__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| name | | Text | False | | | +| description | | Text | True | | | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| services | OtnService | True | many | Attribute | + +### OpticalPath + +- **Label:** Optical path +- **Description:** The materialised chosen route for one service. +- **Namespace:** Otn +- **Icon:** mdi:map-marker-path +- **Uniqueness Constraints:** + - service, segment_sequence__value +- **Human Friendly ID:** name__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| name | | Text | False | | | +| description | | Text | True | | | +| segment_sequence | Which segment of its circuit this path is. 1 for a circuit that spans one wavelength. | Number | False | 1 | | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| service | OtnService | False | one | Parent | +| carrier | OtnOpticalCarrier | True | one | Attribute | +| hops | OtnPathHop | True | many | Component | + +### PathHop + +- **Label:** Path hop +- **Description:** One ordered element on a path. +- **Namespace:** Otn +- **Icon:** mdi:ray-vertex +- **Human Friendly ID:** name__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| name | | Text | False | | | +| sequence | Position along the path, counting from the endpoint A ROADM. | Number | False | | | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| path | OtnOpticalPath | False | one | Parent | +| element | OtnOpticalElement | False | one | Attribute | + +### Facility + +- **Label:** Facility +- **Description:** A supercomputing facility hosted at a PoP. +- **Namespace:** Otn +- **Icon:** ri:cpu-line +- **Human Friendly ID:** name__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| name | | Text | | | | +| description | | Text | True | | | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| site | LocationSite | True | one | Attribute | + +## Generics + +### GenericPort + +- **Label:** Port +- **Description:** Any port on any OTN device. The connection surface of the network. +- **Namespace:** Otn +- **Icon:** mdi:ethernet +- **Uniqueness Constraints:** + - device, name__value +- **Human Friendly ID:** device__name__value, name__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| name | Port identifier as the vendor labels it, such as 1/1/1. | Text | False | | | +| role | | Dropdown | False | spare | client, line, add_drop, degree, booster, preamp, tributary, spare, monitor | +| enabled | | Boolean | False | True | | +| admin_state | RFC 2863 ifAdminStatus. What the operator asked for. | Dropdown | False | up | up, down, testing | +| oper_state | RFC 2863 ifOperStatus subset. What the port actually does. | Dropdown | False | down | up, down, testing, dormant, unknown | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| device | OtnGenericDevice | False | one | Parent | +| connected_to | OtnGenericPort | True | one | Attribute | + +### OpticalElement + +- **Label:** Optical element +- **Description:** Anything light passes through and loses power in. +- **Namespace:** Otn +- **Icon:** mdi:blur-linear + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| insertion_loss_mdb | Insertion loss in millidecibels, 0 to 60 dB. No Float kind exists. | Number | False | 0 | | +| insertion_loss_display | Insertion loss in dB. Read-only, derived from insertion_loss_mdb. | Text | True | | | +| vendor | | Text | True | | | +| model | | Text | True | | | +| element_class | What the optical budget engine branches on. | Dropdown | False | | transponder, roadm, amplifier, mux_demux, patch_panel, fiber_span, splitter, attenuator, raman_pump, odu_switch | + +### GenericDevice + +- **Label:** Device +- **Description:** Anything racked at a site. +- **Namespace:** Otn +- **Icon:** mdi:server +- **Human Friendly ID:** name__value + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| name | | Text | False | | | +| status | | Dropdown | False | active | planned, provisioning, active, maintenance, decommissioned | +| role | | Dropdown | False | | core, edge, access, passive | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| site | LocationSite | True | one | Attribute | +| dcim_device | DcimPhysicalDevice | True | one | Attribute | +| ports | OtnGenericPort | True | many | Component | + +### OpticalPort + +- **Label:** Optical port +- **Description:** Optical-specific port properties. +- **Namespace:** Otn +- **Icon:** mdi:lightbulb-on-outline + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| center_frequency_mhz | ITU-T G.694.1 centre frequency in MHz. No Float kind exists. | Number | True | | | +| center_frequency_display | Centre frequency in THz. Empty on a grey port, which has none. | Text | True | | | +| tx_power_mdbm | Transmit power in milli-dBm, -30 to +30 dBm. | Number | True | | | +| tx_power_display | Transmit power in dBm. Read-only, derived from tx_power_mdbm. | Text | True | | | +| rx_sensitivity_mdbm | Receiver sensitivity in milli-dBm, -40 to +10 dBm. | Number | True | | | +| rx_sensitivity_display | Receiver sensitivity in dBm. Derived from rx_sensitivity_mdbm. | Text | True | | | +| connector_type | | Dropdown | True | | LC, SC, MPO-12, MPO-16, FC, E2000, MU, CS, splice | +| polish | Endface polish. Mating an APC to a UPC costs about 0.5 dB and reflects. | Dropdown | True | | UPC, APC, PC, none | + +### CopperPort + +- **Label:** Copper port +- **Description:** Electrical port properties for G.703 tributaries. +- **Namespace:** Otn +- **Icon:** mdi:cable-data + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| speed_kbps | Line rate in kbps. E1 is 2048, T1 is 1544, STM-1 is 155520. | Number | False | 2048 | | +| impedance_ohm | Nominal impedance in ohms. 120 for E1 pair, 75 for coax. | Number | False | 120 | | +| connector_type | | Dropdown | False | RJ48 | BNC, RJ48, RJ45 | + +## Extensions + +:::note + +In this context "extensions" refer to modifications or additions to the existing schema, such as adding new attributes, relationships, or other schema elements. + +::: + +### LocationSite + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| latitude_microdeg | Latitude in millionths of a degree. No Float kind exists. | Number | True | | | +| longitude_microdeg | Longitude in millionths of a degree. No Float kind exists. | Number | True | | | +| site_type | A PoP hosting OTN equipment, or a customer site handing traffic to one. | Dropdown | False | pop | pop, customer | + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| otn_facility | OtnFacility | True | one | Attribute | +| devices | OtnGenericDevice | True | many | Generic | +| spans_a | OtnFiberSpan | True | many | Attribute | +| spans_b | OtnFiberSpan | True | many | Attribute | + +## Code + +```yaml +version: '1.0' +generics: +- name: GenericPort + namespace: Otn + description: Any port on any OTN device. The connection surface of the network. + label: Port + icon: mdi:ethernet + include_in_menu: false + human_friendly_id: + - device__name__value + - name__value + order_by: + - name__value + display_label: '{{ device__name__value }} {{ name__value }}' + uniqueness_constraints: + - - device + - name__value + attributes: + - name: name + kind: Text + parameters: + max_length: 64 + regex: ^[A-Za-z0-9._/-]+$ + optional: false + description: Port identifier as the vendor labels it, such as 1/1/1. + order_weight: 1000 + - name: role + kind: Dropdown + default_value: spare + choices: + - name: client + label: Client + color: '#4caf50' + - name: line + label: Line + color: '#2196f3' + - name: add_drop + label: Add/drop + color: '#9c27b0' + - name: degree + label: Degree + color: '#673ab7' + - name: booster + label: Booster + color: '#ff9800' + - name: preamp + label: Pre-amplifier + color: '#ffc107' + - name: tributary + label: Tributary + color: '#795548' + - name: spare + label: Spare + color: '#9e9e9e' + - name: monitor + label: Monitor + description: A monitoring interface, not a traffic-carrying port. + color: '#607d8b' + optional: false + order_weight: 1100 + - name: enabled + kind: Boolean + default_value: true + optional: false + order_weight: 1200 + - name: admin_state + kind: Dropdown + default_value: up + choices: + - name: up + label: Up + color: '#4caf50' + - name: down + label: Down + color: '#f44336' + - name: testing + label: Testing + color: '#ff9800' + optional: false + description: RFC 2863 ifAdminStatus. What the operator asked for. + order_weight: 1300 + - name: oper_state + kind: Dropdown + default_value: down + choices: + - name: up + label: Up + color: '#4caf50' + - name: down + label: Down + color: '#f44336' + - name: testing + label: Testing + color: '#ff9800' + - name: dormant + label: Dormant + color: '#2196f3' + - name: unknown + label: Unknown + color: '#9e9e9e' + optional: false + description: RFC 2863 ifOperStatus subset. What the port actually does. + order_weight: 1400 + relationships: + - name: device + peer: OtnGenericDevice + kind: Parent + cardinality: one + optional: false + identifier: otn_device__ports + order_weight: 800 + - name: connected_to + peer: OtnGenericPort + kind: Attribute + cardinality: one + optional: true + identifier: otn_port__connected_to + order_weight: 900 +- name: OpticalElement + namespace: Otn + description: Anything light passes through and loses power in. + label: Optical element + icon: mdi:blur-linear + include_in_menu: false + attributes: + - name: insertion_loss_mdb + kind: Number + default_value: 0 + parameters: + min_value: 0 + max_value: 60000 + optional: false + description: Insertion loss in millidecibels, 0 to 60 dB. No Float kind exists. + order_weight: 1500 + - name: insertion_loss_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if insertion_loss_mdb__value is not none %}{{ insertion_loss_mdb__value + / 1000 }} dB{% endif %}' + optional: true + description: Insertion loss in dB. Read-only, derived from insertion_loss_mdb. + order_weight: 1510 + - name: vendor + kind: Text + parameters: + max_length: 64 + optional: true + order_weight: 1600 + - name: model + kind: Text + parameters: + max_length: 64 + optional: true + order_weight: 1700 + - name: element_class + kind: Dropdown + choices: + - name: transponder + label: Transponder + color: '#2196f3' + - name: roadm + label: ROADM + color: '#9c27b0' + - name: amplifier + label: Amplifier + color: '#ff9800' + - name: mux_demux + label: Mux/demux + color: '#00bcd4' + - name: patch_panel + label: Patch panel + color: '#9e9e9e' + - name: fiber_span + label: Fiber span + color: '#4caf50' + - name: splitter + label: Splitter + color: '#8bc34a' + - name: attenuator + label: Attenuator + color: '#795548' + - name: raman_pump + label: Raman pump + color: '#e91e63' + - name: odu_switch + label: ODU switch + color: '#673ab7' + optional: false + description: What the optical budget engine branches on. + order_weight: 1800 +- name: GenericDevice + namespace: Otn + description: Anything racked at a site. + label: Device + icon: mdi:server + include_in_menu: true + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: ^[A-Za-z0-9._-]+$ + optional: false + order_weight: 1000 + - name: status + kind: Dropdown + default_value: active + choices: + - name: planned + label: Planned + color: '#2196f3' + - name: provisioning + label: Provisioning + color: '#ff9800' + - name: active + label: Active + color: '#4caf50' + - name: maintenance + label: Maintenance + color: '#ffc107' + - name: decommissioned + label: Decommissioned + color: '#9e9e9e' + optional: false + order_weight: 1100 + - name: role + kind: Dropdown + choices: + - name: core + label: Core + color: '#3f51b5' + - name: edge + label: Edge + color: '#2196f3' + - name: access + label: Access + color: '#00bcd4' + - name: passive + label: Passive + color: '#9e9e9e' + optional: false + order_weight: 1200 + relationships: + - name: site + peer: LocationSite + kind: Attribute + cardinality: one + optional: true + identifier: otn_site__devices + order_weight: 800 + - name: dcim_device + peer: DcimPhysicalDevice + label: Physical device record + kind: Attribute + cardinality: one + optional: true + identifier: otn_device__dcim_device + order_weight: 940 + - name: ports + peer: OtnGenericPort + kind: Component + cardinality: many + optional: true + identifier: otn_device__ports + on_delete: cascade + order_weight: 900 +- name: OpticalPort + namespace: Otn + description: Optical-specific port properties. + label: Optical port + icon: mdi:lightbulb-on-outline + include_in_menu: false + attributes: + - name: center_frequency_mhz + kind: Number + parameters: + min_value: 191350000 + max_value: 196100000 + optional: true + description: ITU-T G.694.1 centre frequency in MHz. No Float kind exists. + order_weight: 1600 + - name: center_frequency_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if center_frequency_mhz__value is not none %}{{ center_frequency_mhz__value + / 1000000 }} THz{% endif %}' + optional: true + description: Centre frequency in THz. Empty on a grey port, which has none. + order_weight: 1610 + - name: tx_power_mdbm + kind: Number + parameters: + min_value: -30000 + max_value: 30000 + optional: true + description: Transmit power in milli-dBm, -30 to +30 dBm. + order_weight: 1700 + - name: tx_power_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if tx_power_mdbm__value is not none %}{{ tx_power_mdbm__value + / 1000 }} dBm{% endif %}' + optional: true + description: Transmit power in dBm. Read-only, derived from tx_power_mdbm. + order_weight: 1710 + - name: rx_sensitivity_mdbm + kind: Number + parameters: + min_value: -40000 + max_value: 10000 + optional: true + description: Receiver sensitivity in milli-dBm, -40 to +10 dBm. + order_weight: 1800 + - name: rx_sensitivity_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if rx_sensitivity_mdbm__value is not none %}{{ rx_sensitivity_mdbm__value + / 1000 }} dBm{% endif %}' + optional: true + description: Receiver sensitivity in dBm. Derived from rx_sensitivity_mdbm. + order_weight: 1810 + - name: connector_type + kind: Dropdown + choices: + - name: LC + label: LC + description: Small-form duplex latch. What transponder and pluggable optics + take. + color: '#2196f3' + - name: SC + label: SC + description: Push-pull square ferrule, common on older distribution frames. + color: '#4caf50' + - name: MPO-12 + label: MPO-12 + description: Twelve-fibre ribbon ferrule for parallel optics. + color: '#9c27b0' + - name: MPO-16 + label: MPO-16 + description: Sixteen-fibre ribbon ferrule for 400G parallel optics. + color: '#673ab7' + - name: FC + label: FC + description: Threaded ferrule. Still found on test sets and metro equipment. + color: '#ff9800' + - name: E2000 + label: E2000 + description: Shuttered push-pull connector, widespread on European carrier plant. + color: '#00bcd4' + - name: MU + label: MU + description: Miniature unibody connector, half the footprint of an SC. + color: '#8bc34a' + - name: CS + label: CS + description: Narrow-pitch duplex connector, used for QSFP-DD breakout. + color: '#e91e63' + - name: splice + label: Splice + description: Fusion splice rather than a mated pair. No connector loss and no + polish. + color: '#9e9e9e' + optional: true + order_weight: 1900 + - name: polish + kind: Dropdown + choices: + - name: UPC + label: UPC + description: Ultra physical contact. Blue housing, about 50 dB return loss. + color: '#2196f3' + - name: APC + label: APC + description: Angled physical contact. Green housing, about 65 dB return loss. + color: '#4caf50' + - name: PC + label: PC + description: Physical contact. The oldest polish, about 35 dB return loss. + color: '#ff9800' + - name: none + label: None + description: No polished endface, such as a fusion splice. + color: '#9e9e9e' + optional: true + description: Endface polish. Mating an APC to a UPC costs about 0.5 dB and reflects. + order_weight: 1910 +- name: CopperPort + namespace: Otn + description: Electrical port properties for G.703 tributaries. + label: Copper port + icon: mdi:cable-data + include_in_menu: false + attributes: + - name: speed_kbps + kind: Number + default_value: 2048 + parameters: + min_value: 64 + max_value: 400000000 + optional: false + description: Line rate in kbps. E1 is 2048, T1 is 1544, STM-1 is 155520. + order_weight: 1600 + - name: impedance_ohm + kind: Number + default_value: 120 + parameters: + min_value: 50 + max_value: 600 + optional: false + description: Nominal impedance in ohms. 120 for E1 pair, 75 for coax. + order_weight: 1700 + - name: connector_type + kind: Dropdown + default_value: RJ48 + choices: + - name: BNC + label: BNC + description: Coaxial bayonet connector, used for 75 ohm E1 and STM-1. + color: '#ff9800' + - name: RJ48 + label: RJ48 + description: Eight-position jack for 120 ohm balanced E1 and T1 pairs. + color: '#2196f3' + - name: RJ45 + label: RJ45 + description: Eight-position jack for Ethernet management and console. + color: '#4caf50' + optional: false + order_weight: 1900 +nodes: +- name: RouterPort + namespace: Otn + description: Grey optics on an IP router. Not on the C-band grid. + label: Router port + icon: mdi:ethernet + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnOpticalPort + attributes: + - name: connector_type + kind: Dropdown + choices: + - name: LC + label: LC + description: Small-form duplex latch. What transponder and pluggable optics + take. + color: '#2196f3' + - name: SC + label: SC + description: Push-pull square ferrule, common on older distribution frames. + color: '#4caf50' + - name: MPO-12 + label: MPO-12 + description: Twelve-fibre ribbon ferrule for parallel optics. + color: '#9c27b0' + - name: MPO-16 + label: MPO-16 + description: Sixteen-fibre ribbon ferrule for 400G parallel optics. + color: '#673ab7' + - name: FC + label: FC + description: Threaded ferrule. Still found on test sets and metro equipment. + color: '#ff9800' + - name: E2000 + label: E2000 + description: Shuttered push-pull connector, widespread on European carrier plant. + color: '#00bcd4' + - name: MU + label: MU + description: Miniature unibody connector, half the footprint of an SC. + color: '#8bc34a' + - name: CS + label: CS + description: Narrow-pitch duplex connector, used for QSFP-DD breakout. + color: '#e91e63' + - name: splice + label: Splice + description: Fusion splice rather than a mated pair. No connector loss and no + polish. + color: '#9e9e9e' + optional: true + order_weight: 1900 + relationships: + - name: transceiver + peer: OtnTransceiver + label: Module fitted in this port + kind: Attribute + cardinality: one + optional: true + identifier: otn_optical_port__transceiver + order_weight: 960 +- name: ClientPort + namespace: Otn + description: Transponder client side. Where grey light arrives. + label: Client port + icon: mdi:lan-connect + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnOpticalPort + attributes: + - name: connector_type + kind: Dropdown + choices: + - name: LC + label: LC + description: Small-form duplex latch. What transponder and pluggable optics + take. + color: '#2196f3' + - name: SC + label: SC + description: Push-pull square ferrule, common on older distribution frames. + color: '#4caf50' + - name: MPO-12 + label: MPO-12 + description: Twelve-fibre ribbon ferrule for parallel optics. + color: '#9c27b0' + - name: MPO-16 + label: MPO-16 + description: Sixteen-fibre ribbon ferrule for 400G parallel optics. + color: '#673ab7' + - name: FC + label: FC + description: Threaded ferrule. Still found on test sets and metro equipment. + color: '#ff9800' + - name: E2000 + label: E2000 + description: Shuttered push-pull connector, widespread on European carrier plant. + color: '#00bcd4' + - name: MU + label: MU + description: Miniature unibody connector, half the footprint of an SC. + color: '#8bc34a' + - name: CS + label: CS + description: Narrow-pitch duplex connector, used for QSFP-DD breakout. + color: '#e91e63' + - name: splice + label: Splice + description: Fusion splice rather than a mated pair. No connector loss and no + polish. + color: '#9e9e9e' + optional: true + order_weight: 1900 + relationships: + - name: transceiver + peer: OtnTransceiver + label: Module fitted in this port + kind: Attribute + cardinality: one + optional: true + identifier: otn_optical_port__transceiver + order_weight: 960 +- name: LinePort + namespace: Otn + description: Transponder DWDM line side. Where coloured light leaves. + label: Line port + icon: mdi:transit-connection + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnOpticalPort + attributes: + - name: connector_type + kind: Dropdown + choices: + - name: LC + label: LC + description: Small-form duplex latch. What transponder and pluggable optics + take. + color: '#2196f3' + - name: SC + label: SC + description: Push-pull square ferrule, common on older distribution frames. + color: '#4caf50' + - name: MPO-12 + label: MPO-12 + description: Twelve-fibre ribbon ferrule for parallel optics. + color: '#9c27b0' + - name: MPO-16 + label: MPO-16 + description: Sixteen-fibre ribbon ferrule for 400G parallel optics. + color: '#673ab7' + - name: FC + label: FC + description: Threaded ferrule. Still found on test sets and metro equipment. + color: '#ff9800' + - name: E2000 + label: E2000 + description: Shuttered push-pull connector, widespread on European carrier plant. + color: '#00bcd4' + - name: MU + label: MU + description: Miniature unibody connector, half the footprint of an SC. + color: '#8bc34a' + - name: CS + label: CS + description: Narrow-pitch duplex connector, used for QSFP-DD breakout. + color: '#e91e63' + - name: splice + label: Splice + description: Fusion splice rather than a mated pair. No connector loss and no + polish. + color: '#9e9e9e' + optional: true + order_weight: 1900 + relationships: + - name: carrier + peer: OtnOpticalCarrier + label: Wavelength this port terminates + kind: Attribute + cardinality: one + optional: true + identifier: otn_carrier__line_ports + order_weight: 850 + - name: transceiver + peer: OtnTransceiver + label: Module fitted in this port + kind: Attribute + cardinality: one + optional: true + identifier: otn_optical_port__transceiver + order_weight: 960 +- name: RoadmAddDropPort + namespace: Otn + description: Local add and drop on a ROADM. + label: ROADM add/drop port + icon: mdi:call-split + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnOpticalPort + attributes: + - name: connector_type + kind: Dropdown + choices: + - name: LC + label: LC + description: Small-form duplex latch. What transponder and pluggable optics + take. + color: '#2196f3' + - name: SC + label: SC + description: Push-pull square ferrule, common on older distribution frames. + color: '#4caf50' + - name: MPO-12 + label: MPO-12 + description: Twelve-fibre ribbon ferrule for parallel optics. + color: '#9c27b0' + - name: MPO-16 + label: MPO-16 + description: Sixteen-fibre ribbon ferrule for 400G parallel optics. + color: '#673ab7' + - name: FC + label: FC + description: Threaded ferrule. Still found on test sets and metro equipment. + color: '#ff9800' + - name: E2000 + label: E2000 + description: Shuttered push-pull connector, widespread on European carrier plant. + color: '#00bcd4' + - name: MU + label: MU + description: Miniature unibody connector, half the footprint of an SC. + color: '#8bc34a' + - name: CS + label: CS + description: Narrow-pitch duplex connector, used for QSFP-DD breakout. + color: '#e91e63' + - name: splice + label: Splice + description: Fusion splice rather than a mated pair. No connector loss and no + polish. + color: '#9e9e9e' + optional: true + order_weight: 1900 +- name: RoadmDegreePort + namespace: Otn + description: Line-facing degree on a ROADM. One per direction. + label: ROADM degree port + icon: mdi:compass-outline + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnOpticalPort + attributes: + - name: connector_type + kind: Dropdown + choices: + - name: LC + label: LC + description: Small-form duplex latch. What transponder and pluggable optics + take. + color: '#2196f3' + - name: SC + label: SC + description: Push-pull square ferrule, common on older distribution frames. + color: '#4caf50' + - name: MPO-12 + label: MPO-12 + description: Twelve-fibre ribbon ferrule for parallel optics. + color: '#9c27b0' + - name: MPO-16 + label: MPO-16 + description: Sixteen-fibre ribbon ferrule for 400G parallel optics. + color: '#673ab7' + - name: FC + label: FC + description: Threaded ferrule. Still found on test sets and metro equipment. + color: '#ff9800' + - name: E2000 + label: E2000 + description: Shuttered push-pull connector, widespread on European carrier plant. + color: '#00bcd4' + - name: MU + label: MU + description: Miniature unibody connector, half the footprint of an SC. + color: '#8bc34a' + - name: CS + label: CS + description: Narrow-pitch duplex connector, used for QSFP-DD breakout. + color: '#e91e63' + - name: splice + label: Splice + description: Fusion splice rather than a mated pair. No connector loss and no + polish. + color: '#9e9e9e' + optional: true + order_weight: 1900 +- name: AmplifierPort + namespace: Otn + description: Amplifier input or output. + label: Amplifier port + icon: mdi:amplifier + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnOpticalPort + attributes: + - name: connector_type + kind: Dropdown + choices: + - name: LC + label: LC + description: Small-form duplex latch. What transponder and pluggable optics + take. + color: '#2196f3' + - name: SC + label: SC + description: Push-pull square ferrule, common on older distribution frames. + color: '#4caf50' + - name: MPO-12 + label: MPO-12 + description: Twelve-fibre ribbon ferrule for parallel optics. + color: '#9c27b0' + - name: MPO-16 + label: MPO-16 + description: Sixteen-fibre ribbon ferrule for 400G parallel optics. + color: '#673ab7' + - name: FC + label: FC + description: Threaded ferrule. Still found on test sets and metro equipment. + color: '#ff9800' + - name: E2000 + label: E2000 + description: Shuttered push-pull connector, widespread on European carrier plant. + color: '#00bcd4' + - name: MU + label: MU + description: Miniature unibody connector, half the footprint of an SC. + color: '#8bc34a' + - name: CS + label: CS + description: Narrow-pitch duplex connector, used for QSFP-DD breakout. + color: '#e91e63' + - name: splice + label: Splice + description: Fusion splice rather than a mated pair. No connector loss and no + polish. + color: '#9e9e9e' + optional: true + order_weight: 1900 +- name: MuxClientPort + namespace: Otn + description: One channel of a multiplexer. The side facing the transponder or router + that lights it. + label: Mux client port + icon: mdi:import + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnOpticalPort + relationships: + - name: dwdm_channel + peer: OtnFrequencyGrid + label: Dense channel this port carries + kind: Attribute + cardinality: one + optional: true + identifier: otn_frequency_grid__mux_client_ports + order_weight: 850 + - name: cwdm_channel + peer: OtnCwdmChannel + label: Coarse wavelength this port carries + kind: Attribute + cardinality: one + optional: true + identifier: otn_cwdm_channel__mux_client_ports + order_weight: 860 +- name: MuxLinePort + namespace: Otn + description: The common side of a multiplexer, where the whole band leaves. + label: Mux line port + icon: mdi:export + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnOpticalPort +- name: TributaryPort + namespace: Otn + description: E1 or T1 G.703 electrical tributary. The only copper port here. + label: Tributary port + icon: mdi:cable-data + include_in_menu: false + inherit_from: + - OtnGenericPort + - OtnCopperPort +- name: TransceiverType + namespace: Otn + description: A pluggable optic part number and what it can be made to do. + label: Transceiver type + icon: mdi:chip + include_in_menu: true + human_friendly_id: + - part_number__value + order_by: + - part_number__value + display_label: part_number__value + attributes: + - name: part_number + kind: Text + unique: true + parameters: + max_length: 64 + regex: ^[A-Za-z0-9._/+-]+$ + optional: false + description: Vendor part number, such as QDD-400G-ZRP-S. + order_weight: 1000 + - name: vendor + kind: Text + parameters: + max_length: 64 + optional: true + order_weight: 1100 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1200 + - name: form_factor + kind: Dropdown + choices: + - name: QSFP-DD + label: QSFP-DD + description: Eight-lane double-density cage. What a coherent 400G pluggable + takes. + color: '#2196f3' + - name: QSFP28 + label: QSFP28 + description: Four-lane cage, 100G grey optics. + color: '#4caf50' + - name: OSFP + label: OSFP + description: Eight-lane cage with a larger thermal budget than QSFP-DD. + color: '#9c27b0' + - name: CFP2-DCO + label: CFP2-DCO + description: Digital coherent optic. The DSP is in the module. + color: '#ff9800' + - name: CFP2-ACO + label: CFP2-ACO + description: Analogue coherent optic. The DSP is on the host card. + color: '#ffc107' + - name: SFP28 + label: SFP28 + description: Single-lane 25G cage. + color: '#00bcd4' + - name: SFP+ + label: SFP+ + description: Single-lane 10G cage. + color: '#9e9e9e' + optional: false + description: The cage this part fits. It decides which ports can hold it. + order_weight: 1300 + - name: tunable + kind: Boolean + default_value: false + optional: false + description: Whether the laser can be tuned across the grid or is fixed at one + wavelength. + order_weight: 1400 + relationships: + - name: supported_modes + peer: OtnOpticalMode + label: Modes this part can run + kind: Attribute + cardinality: many + optional: true + identifier: otn_optical_mode__transceiver_types + order_weight: 900 +- name: Transceiver + namespace: Otn + description: One physical pluggable optic, fitted in a port or sitting on a shelf. + label: Transceiver + icon: mdi:memory + include_in_menu: true + human_friendly_id: + - serial__value + order_by: + - serial__value + display_label: '{{ type__part_number__value }} {{ serial__value }}' + attributes: + - name: serial + kind: Text + unique: true + parameters: + max_length: 64 + regex: ^[A-Za-z0-9._-]+$ + optional: false + description: Vendor serial number. The only thing that identifies this unit. + order_weight: 1000 + - name: status + kind: Dropdown + default_value: spare + choices: + - name: in_service + label: In service + color: '#4caf50' + - name: spare + label: Spare + color: '#2196f3' + - name: rma + label: Returned to vendor + color: '#ff9800' + - name: decommissioned + label: Decommissioned + color: '#9e9e9e' + optional: false + order_weight: 1100 + relationships: + - name: type + peer: OtnTransceiverType + label: Part this unit is + kind: Attribute + cardinality: one + optional: false + identifier: otn_transceiver_type__units + order_weight: 900 + - name: port + peer: OtnOpticalPort + label: Port this unit is fitted in + kind: Attribute + cardinality: one + optional: true + identifier: otn_optical_port__transceiver + order_weight: 950 +- name: FiberType + namespace: Otn + description: Single-mode fiber family. Attenuation, dispersion and group index at + 1550 nm. + label: Fiber type + icon: mdi:cable-data + include_in_menu: true + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 32 + regex: ^[A-Za-z0-9.-]+$ + optional: false + description: ITU-T designation, such as G.652.D. + order_weight: 1000 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1100 + - name: attenuation_coefficient_mdb_per_km + kind: Number + parameters: + min_value: 150 + max_value: 400 + optional: false + description: Attenuation at 1550 nm in millidecibels per kilometre. 0.20 dB/km + is 200. Wrong for CWDM. + order_weight: 1500 + - name: attenuation_coefficient_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if attenuation_coefficient_mdb_per_km__value is not none + %}{{ attenuation_coefficient_mdb_per_km__value / 1000 }} dB/km{% endif %}' + optional: true + description: Attenuation in dB/km. Read-only, derived from attenuation_coefficient_mdb_per_km. + order_weight: 1510 + - name: dispersion_fs_per_nm_km + kind: Number + parameters: + min_value: 0 + max_value: 30000 + optional: false + description: Chromatic dispersion at 1550 nm in femtoseconds per nm per km. 17 + ps/nm/km is 17000. + order_weight: 1600 + - name: dispersion_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if dispersion_fs_per_nm_km__value is not none %}{{ dispersion_fs_per_nm_km__value + / 1000 }} ps/nm/km{% endif %}' + optional: true + description: Dispersion in ps/nm/km. Read-only, derived from dispersion_fs_per_nm_km. + order_weight: 1610 + - name: group_index_milli + kind: Number + default_value: 1468 + parameters: + min_value: 1400 + max_value: 1500 + optional: false + description: Group index at 1550 nm in milli-units. 1.468 is 1468. Sets propagation + delay. + order_weight: 1700 + relationships: + - name: spans + peer: OtnFiberSpan + kind: Attribute + cardinality: many + optional: true + identifier: otn_fiber_type__spans + order_weight: 900 +- name: Conduit + namespace: Otn + description: Shared-risk link group. Two routes through one conduit are not diverse. + label: Conduit + icon: mdi:pipe + include_in_menu: true + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: ^[A-Za-z0-9._-]+$ + optional: false + order_weight: 1000 + - name: owner + kind: Text + parameters: + max_length: 64 + optional: true + description: Who owns the trench. Often not the network operator. + order_weight: 1100 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1200 + relationships: + - name: spans + peer: OtnFiberSpan + kind: Attribute + cardinality: many + optional: true + identifier: otn_conduit__spans + order_weight: 900 +- name: FiberSpan + namespace: Otn + description: One amplifier-to-amplifier fiber section. Lossy, but not racked and + not a device. + label: Fiber span + icon: mdi:vector-line + include_in_menu: true + inherit_from: + - OtnOpticalElement + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: ^[A-Za-z0-9._-]+$ + optional: false + order_weight: 1000 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1100 + - name: oms_sequence + kind: Number + parameters: + min_value: 1 + max_value: 50 + optional: true + description: Position of this span within its optical multiplex section, counting + from the A end. + order_weight: 1200 + - name: length_m + kind: Number + parameters: + min_value: 0 + max_value: 500000 + optional: false + description: Route length in metres, not straight-line distance. 500 km is the + practical ceiling. + order_weight: 1500 + - name: length_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if length_m__value is not none %}{{ length_m__value / 1000 + }} km{% endif %}' + optional: true + description: Route length in km. Read-only, derived from length_m. + order_weight: 1510 + - name: splice_count + kind: Number + default_value: 0 + parameters: + min_value: 0 + max_value: 500 + optional: false + description: Fusion splices along the span. Roughly one per drum of cable. + order_weight: 1600 + - name: splice_loss_mdb + kind: Number + default_value: 50 + parameters: + min_value: 0 + max_value: 1000 + optional: false + description: Loss per splice in millidecibels. 0.05 dB is 50. + order_weight: 1610 + - name: connector_count + kind: Number + default_value: 2 + parameters: + min_value: 0 + max_value: 20 + optional: false + description: Mated connector pairs on the span, normally one at each end. + order_weight: 1700 + - name: connector_loss_mdb + kind: Number + default_value: 300 + parameters: + min_value: 0 + max_value: 2000 + optional: false + description: Loss per mated connector pair in millidecibels. 0.3 dB is 300. + order_weight: 1710 + - name: aging_margin_mdb + kind: Number + default_value: 1500 + parameters: + min_value: 0 + max_value: 5000 + optional: false + description: Reserved margin for repairs and ageing, in millidecibels. 1.5 dB + is 1500. + order_weight: 1720 + relationships: + - name: fiber_type + peer: OtnFiberType + kind: Attribute + cardinality: one + optional: false + identifier: otn_fiber_type__spans + order_weight: 800 + - name: site_a + peer: LocationSite + kind: Attribute + cardinality: one + optional: false + identifier: otn_span__site_a + order_weight: 810 + - name: site_b + peer: LocationSite + kind: Attribute + cardinality: one + optional: false + identifier: otn_span__site_b + order_weight: 820 + - name: conduit + peer: OtnConduit + kind: Attribute + cardinality: one + optional: true + identifier: otn_conduit__spans + order_weight: 830 + - name: oms + peer: OtnOpticalMultiplexSection + kind: Attribute + cardinality: one + optional: true + identifier: otn_oms__spans + order_weight: 840 + - name: raman_pumps + peer: OtnRamanPump + kind: Attribute + cardinality: many + optional: true + identifier: otn_span__raman_pumps + order_weight: 850 + - name: terminating_ports + peer: OtnGenericPort + label: Ports at the ends of this span + kind: Attribute + cardinality: many + optional: true + identifier: otn_span__terminating_ports + order_weight: 950 +- name: OpticalMultiplexSection + namespace: Otn + description: ROADM to ROADM. Groups the ordered spans and inline amplifiers between + two degrees. + label: Optical multiplex section + icon: mdi:ray-start-end + include_in_menu: true + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: ^[A-Za-z0-9._-]+$ + optional: false + order_weight: 1000 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1100 + relationships: + - name: roadm_a + peer: OtnRoadm + kind: Attribute + cardinality: one + optional: false + identifier: otn_oms__roadm_a + order_weight: 800 + - name: roadm_b + peer: OtnRoadm + kind: Attribute + cardinality: one + optional: false + identifier: otn_oms__roadm_b + order_weight: 810 + - name: spans + peer: OtnFiberSpan + kind: Attribute + cardinality: many + optional: true + identifier: otn_oms__spans + order_weight: 900 + - name: amplifiers_a2b + peer: OtnAmplifier + kind: Attribute + cardinality: many + optional: true + identifier: otn_oms__amplifiers_a2b + order_weight: 910 + - name: amplifiers_b2a + peer: OtnAmplifier + kind: Attribute + cardinality: many + optional: true + identifier: otn_oms__amplifiers_b2a + order_weight: 920 +- name: FrequencyGrid + namespace: Otn + description: One ITU-T G.694.1 channel. 50 GHz fixed grid, 191.35 to 196.10 THz. + label: Frequency grid channel + icon: mdi:sine-wave + include_in_menu: true + human_friendly_id: + - channel_number__value + order_by: + - channel_number__value + display_label: Ch{{ channel_number__value }} + attributes: + - name: channel_number + kind: Number + unique: true + parameters: + min_value: 1 + max_value: 96 + optional: false + description: ITU channel number, 1 to 96. Channel 1 is 191.35 THz. + order_weight: 1000 + - name: center_frequency_mhz + kind: Number + unique: true + parameters: + min_value: 191350000 + max_value: 196100000 + optional: false + description: Centre frequency in MHz. Channel n is 191350000 + (n - 1) x 50000. + order_weight: 1500 + - name: center_frequency_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if center_frequency_mhz__value is not none %}{{ center_frequency_mhz__value + / 1000000 }} THz{% endif %}' + optional: true + description: Centre frequency in THz. Byte-identical to the optical-port rendering. + order_weight: 1510 + relationships: + - name: carriers + peer: OtnOpticalCarrier + label: Carriers on this channel + kind: Attribute + cardinality: many + optional: true + identifier: otn_carrier__channel + order_weight: 900 +- name: CwdmChannel + namespace: Otn + description: One ITU-T G.694.2 coarse wavelength. 20 nm spacing, 1271 to 1611 nm. + label: CWDM wavelength + icon: mdi:palette-swatch-variant + include_in_menu: true + human_friendly_id: + - center_wavelength_nm__value + order_by: + - center_wavelength_nm__value + display_label: '{{ center_wavelength_nm__value }} nm' + attributes: + - name: center_wavelength_nm + kind: Number + unique: true + parameters: + min_value: 1271 + max_value: 1611 + optional: false + description: Nominal central wavelength in nm. Wavelength n is 1271 + (n - 1) + x 20. + order_weight: 1000 + - name: band + kind: Dropdown + choices: + - name: o + label: O band + color: '#607d8b' + - name: e + label: E band + color: '#795548' + - name: s + label: S band + color: '#009688' + - name: c + label: C band + color: '#2196f3' + - name: l + label: L band + color: '#9c27b0' + optional: false + description: ITU band. Only the two C-band wavelengths sit in the erbium window. + order_weight: 1100 +- name: OpticalMode + namespace: Otn + description: What a transponder or coherent pluggable can do. Reach and required + OSNR are data, not assumptions. + label: Optical mode + icon: mdi:waveform + include_in_menu: true + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: ^[A-Za-z0-9 .+/_-]+$ + optional: false + order_weight: 1000 + - name: mode_class + kind: Dropdown + default_value: transponder + choices: + - name: transponder + label: Transponder + color: '#2196f3' + - name: pluggable + label: Coherent pluggable + color: '#4caf50' + optional: false + description: Two values, and the pluggable-reach report filters on it. + order_weight: 1100 + - name: modulation + kind: Text + enum: + - DP-QPSK + - DP-8QAM + - DP-16QAM + - DP-64QAM + optional: false + order_weight: 1200 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1300 + - name: line_rate_gbps + kind: Number + parameters: + min_value: 1 + max_value: 1600 + optional: false + description: Client-side capacity in Gbps. Already a whole number in its natural + unit. + order_weight: 1500 + - name: baud_mbaud + kind: Number + parameters: + min_value: 1000 + max_value: 200000 + optional: false + description: Symbol rate in megabaud. 59.84 GBd is 59840. Sets the spectral width. + order_weight: 1600 + - name: required_osnr_mdb + kind: Number + parameters: + min_value: 5000 + max_value: 40000 + optional: false + description: OSNR needed at the receiver, in millidecibels. 26 dB is 26000. + order_weight: 1700 + - name: required_osnr_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if required_osnr_mdb__value is not none %}{{ required_osnr_mdb__value + / 1000 }} dB{% endif %}' + optional: true + description: Required OSNR in dB. Read-only, derived from required_osnr_mdb. + order_weight: 1710 + - name: cd_tolerance_fs_per_nm + kind: Number + parameters: + min_value: 0 + max_value: 200000000 + optional: false + description: Chromatic dispersion the receiver compensates, in fs/nm. 100000 ps/nm + is 100000000. + order_weight: 1800 + - name: nominal_reach_m + kind: Number + parameters: + min_value: 0 + max_value: 5000000 + optional: false + description: Vendor-quoted reach in metres. A starting point for the budget, not + a guarantee. + order_weight: 1900 + - name: nominal_reach_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if nominal_reach_m__value is not none %}{{ nominal_reach_m__value + / 1000 }} km{% endif %}' + optional: true + description: Nominal reach in km. Read-only, derived from nominal_reach_m. + order_weight: 1910 + - name: fec_type + kind: Text + default_value: SD-FEC + enum: + - none + - GFEC + - SD-FEC + - cFEC + - oFEC + optional: false + order_weight: 1950 + - name: fec_latency_ns + kind: Number + default_value: 0 + parameters: + min_value: 0 + max_value: 100000 + optional: false + description: Encoder and decoder latency in nanoseconds. Small next to propagation, + but not zero. + order_weight: 1960 + relationships: + - name: carriers + peer: OtnOpticalCarrier + label: Carriers using this mode + kind: Attribute + cardinality: many + optional: true + identifier: otn_carrier__optical_mode + order_weight: 900 +- name: ClientSignal + namespace: Otn + description: What a customer hands over, and the container it maps into. + label: Client signal + icon: mdi:import + include_in_menu: true + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 32 + regex: ^[A-Za-z0-9./_-]+$ + optional: false + order_weight: 1000 + - name: alias + kind: Text + parameters: + max_length: 32 + optional: true + description: The SONET name where one exists. STM-16 is OC-48. + order_weight: 1100 + - name: layer + kind: Dropdown + choices: + - name: ethernet + label: Ethernet + color: '#2196f3' + - name: sdh + label: SDH / SONET + color: '#9c27b0' + - name: pdh + label: PDH + color: '#795548' + - name: fibre_channel + label: Fibre Channel + color: '#ff9800' + - name: infiniband + label: InfiniBand + color: '#673ab7' + optional: false + description: Grouping the catalog by layer is the first thing a reader does. + order_weight: 1200 + - name: auto_selectable + kind: Boolean + default_value: false + optional: false + description: May the rate rule pick this signal when a service states none. + order_weight: 1250 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1300 + - name: bit_rate_kbps + kind: Number + parameters: + min_value: 64 + max_value: 1600000000 + optional: false + description: Nominal line rate in kbps. E1 is 2048, 100GBASE-LR4 is 103100000. + order_weight: 1500 + - name: bit_rate_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if bit_rate_kbps__value is none %}{% elif bit_rate_kbps__value + >= 1000000 %}{{ bit_rate_kbps__value / 1000000 }} Gbps{% else %}{{ bit_rate_kbps__value + / 1000 }} Mbps{% endif %}' + optional: true + description: Bit rate in Mbps below one gigabit, Gbps at or above it. + order_weight: 1510 + - name: default_container_type + kind: Text + enum: + - ODU0 + - ODU1 + - ODU2 + - ODU2e + - ODU3 + - ODU4 + - ODUC1 + - ODUC2 + - ODUC3 + - ODUC4 + - ODUflex + - VC-12 + - VC-4 + - STM-N + optional: false + description: The first step of the mapping chain, not the last. E1 maps into VC-12, + not ODU1. + order_weight: 1600 + - name: default_mapping + kind: Text + enum: + - GMP + - BMP + - AMP + optional: false + description: ITU-T G.709 mapping procedure. Generic, bit-synchronous or asynchronous. + order_weight: 1700 + relationships: + - name: containers + peer: OtnContainer + kind: Attribute + cardinality: many + optional: true + identifier: otn_client_signal__containers + order_weight: 900 + - name: services + peer: OtnService + kind: Attribute + cardinality: many + optional: true + identifier: otn_service__client_signal + order_weight: 910 +- name: Container + namespace: Otn + description: ODU or SDH virtual container. The digital adaptation layer between + a client and a wavelength. + label: Container + icon: mdi:package-variant-closed + include_in_menu: true + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: ^[A-Za-z0-9._-]+$ + optional: false + order_weight: 1000 + - name: odu_type + kind: Text + enum: + - ODU0 + - ODU1 + - ODU2 + - ODU2e + - ODU3 + - ODU4 + - ODUC1 + - ODUC2 + - ODUC3 + - ODUC4 + - ODUC6 + - ODUC8 + - ODUflex + - VC-12 + - VC-4 + - STM-N + optional: false + order_weight: 1100 + - name: mapping_mode + kind: Text + default_value: GMP + enum: + - GMP + - BMP + - AMP + optional: false + order_weight: 1200 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1300 + - name: segment_sequence + kind: Number + default_value: 1 + parameters: + min_value: 1 + max_value: 50 + optional: false + description: Which segment of its circuit this container rides. 1 for a circuit + that spans one wavelength. + order_weight: 1400 + - name: tributary_slots + kind: Number + default_value: 0 + parameters: + min_value: 0 + max_value: 640 + optional: false + description: Slots this container occupies in its parent. An ODU2 in an ODU4 takes + 8. + order_weight: 1500 + - name: tributary_slot_capacity + kind: Number + default_value: 0 + parameters: + min_value: 0 + max_value: 640 + optional: false + description: Slots this container offers to its children. An ODU4 offers 80. + order_weight: 1600 + relationships: + - name: carrier + peer: OtnOpticalCarrier + kind: Attribute + cardinality: one + optional: true + identifier: otn_carrier__containers + order_weight: 790 + - name: client_signal + peer: OtnClientSignal + kind: Attribute + cardinality: one + optional: true + identifier: otn_client_signal__containers + order_weight: 800 + - name: parent_container + peer: OtnContainer + kind: Attribute + cardinality: one + optional: true + identifier: otn_container__children + direction: outbound + order_weight: 810 + - name: service + peer: OtnService + kind: Attribute + cardinality: one + optional: true + identifier: otn_service__containers + order_weight: 820 + - name: child_containers + peer: OtnContainer + kind: Attribute + cardinality: many + optional: true + identifier: otn_container__children + direction: inbound + order_weight: 900 +- name: Router + namespace: Otn + description: IP router. Light terminates here, so it contributes no insertion loss. + label: Router + icon: mdi:router + include_in_menu: true + menu_placement: OtnGenericDevice + inherit_from: + - OtnGenericDevice +- name: Transponder + namespace: Otn + description: Client to DWDM line adaptation. + label: Transponder + icon: mdi:transit-connection-variant + include_in_menu: true + menu_placement: OtnGenericDevice + inherit_from: + - OtnGenericDevice + - OtnOpticalElement +- name: Roadm + namespace: Otn + description: Reconfigurable optical add/drop multiplexer. + label: ROADM + icon: mdi:call-split + include_in_menu: true + menu_placement: OtnGenericDevice + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + relationships: + - name: sections_a + peer: OtnOpticalMultiplexSection + label: Sections (A end) + kind: Attribute + cardinality: many + optional: true + identifier: otn_oms__roadm_a + order_weight: 960 + - name: sections_b + peer: OtnOpticalMultiplexSection + label: Sections (B end) + kind: Attribute + cardinality: many + optional: true + identifier: otn_oms__roadm_b + order_weight: 970 +- name: Amplifier + namespace: Otn + description: Inline, booster or pre-amplifier. + label: Amplifier + icon: mdi:amplifier + include_in_menu: true + menu_placement: OtnGenericDevice + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + attributes: + - name: noise_figure_mdb + kind: Number + default_value: 4000 + parameters: + min_value: 3000 + max_value: 10000 + optional: false + description: Noise figure in millidecibels. 4.0 dB is 4000. Sets the ASE this + stage adds. + order_weight: 1520 + - name: noise_figure_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if noise_figure_mdb__value is not none %}{{ noise_figure_mdb__value + / 1000 }} dB{% endif %}' + optional: true + description: Noise figure in dB. Read-only, derived from noise_figure_mdb. + order_weight: 1530 + - name: gain_mdb + kind: Number + default_value: 22000 + parameters: + min_value: 0 + max_value: 40000 + optional: false + description: Gain in millidecibels. 22.0 dB is 22000. Must cover the loss ahead + of the input. + order_weight: 1540 + - name: gain_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if gain_mdb__value is not none %}{{ gain_mdb__value / 1000 + }} dB{% endif %}' + optional: true + description: Gain in dB. Read-only, derived from gain_mdb. + order_weight: 1550 + - name: oms_sequence + kind: Number + parameters: + min_value: 1 + max_value: 51 + optional: false + description: Position in this amplifier's own chain, counting along the direction + it amplifies. + order_weight: 1560 + relationships: + - name: oms_a2b + peer: OtnOpticalMultiplexSection + kind: Attribute + cardinality: one + optional: true + identifier: otn_oms__amplifiers_a2b + order_weight: 950 + - name: oms_b2a + peer: OtnOpticalMultiplexSection + kind: Attribute + cardinality: one + optional: true + identifier: otn_oms__amplifiers_b2a + order_weight: 960 +- name: MuxDemux + namespace: Otn + description: Passive multiplexer. A dense AWG on the core, a coarse thin-film filter + on a tail. + label: Mux/demux + icon: mdi:call-merge + include_in_menu: true + menu_placement: OtnGenericDevice + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + relationships: + - name: cwdm_channels + peer: OtnCwdmChannel + kind: Attribute + cardinality: many + optional: true + identifier: otn_mux_demux__cwdm_channels + order_weight: 1900 +- name: PatchPanel + namespace: Otn + description: Optical distribution frame. Carries connector loss. + label: Patch panel + icon: mdi:view-grid-outline + include_in_menu: true + menu_placement: OtnGenericDevice + inherit_from: + - OtnGenericDevice + - OtnOpticalElement +- name: RamanPump + namespace: Otn + description: Pump laser injecting Raman gain into one fiber span. + label: Raman pump + icon: mdi:laser-pointer + include_in_menu: true + menu_placement: OtnGenericDevice + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + attributes: + - name: on_off_gain_mdb + kind: Number + default_value: 10000 + parameters: + min_value: 0 + max_value: 15000 + optional: false + description: Raman on-off gain in millidecibels. 10.0 dB is 10000. + order_weight: 1520 + - name: on_off_gain_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if on_off_gain_mdb__value is not none %}{{ on_off_gain_mdb__value + / 1000 }} dB{% endif %}' + optional: true + description: On-off gain in dB. Read-only, derived from on_off_gain_mdb. + order_weight: 1530 + - name: injection_end + kind: Dropdown + default_value: site_b + choices: + - name: site_a + label: Site A end + color: '#2196f3' + - name: site_b + label: Site B end + color: '#ff5722' + optional: false + description: Which end of its span the pump is injected at, named against the + span's own site_a and site_b. + order_weight: 1540 + - name: propagation + kind: Dropdown + default_value: counter + choices: + - name: counter + label: Counter-propagating + color: '#3f51b5' + - name: co + label: Co-propagating + color: '#009688' + optional: false + description: Whether the pump fires against the signal or with it. + order_weight: 1550 + relationships: + - name: span + peer: OtnFiberSpan + kind: Attribute + cardinality: one + optional: false + identifier: otn_span__raman_pumps + order_weight: 850 +- name: OduSwitch + namespace: Otn + description: O-E-O device. Terminates one wavelength, originates the next, as a + regenerator or ODU cross-connect. + label: ODU switch + icon: mdi:swap-horizontal-variant + include_in_menu: true + menu_placement: OtnGenericDevice + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + attributes: + - name: switching_mode + kind: Dropdown + default_value: regenerator + choices: + - name: regenerator + label: Regenerator + color: '#3f51b5' + - name: cross_connect + label: ODU cross-connect + color: '#009688' + optional: false + description: Whether the device carries the whole payload through or demultiplexes + and regroups containers. + order_weight: 1300 + - name: framing_latency_ns + kind: Number + default_value: 0 + parameters: + min_value: 0 + max_value: 100000 + optional: false + description: Delay this device adds in nanoseconds, from framing and from the + electrical crossing. + order_weight: 1520 + relationships: + - name: carriers + peer: OtnOpticalCarrier + label: Carriers patched to this shelf + kind: Attribute + cardinality: many + optional: true + identifier: otn_odu_switch__carriers + order_weight: 1900 +- name: FixedAttenuator + namespace: Otn + description: A pad. One fixed amount of loss, patched into a link that arrives too + hot. + label: Fixed attenuator + icon: mdi:filter-outline + include_in_menu: true + menu_placement: OtnGenericDevice + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + attributes: + - name: attenuation_mdb + kind: Number + default_value: 0 + parameters: + min_value: 0 + max_value: 30000 + optional: false + description: Attenuation the pad is set to, in millidecibels. 5.0 dB is 5000. + order_weight: 1900 + - name: attenuation_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if attenuation_mdb__value is not none %}{{ attenuation_mdb__value + / 1000 }} dB{% endif %}' + optional: true + description: Attenuation in dB. Read-only, derived from attenuation_mdb. + order_weight: 1910 +- name: VariableAttenuator + namespace: Otn + description: A VOA. The same loss, dialled rather than fixed, within the range the + hardware has. + label: Variable attenuator + icon: mdi:tune-variant + include_in_menu: true + menu_placement: OtnGenericDevice + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + attributes: + - name: attenuation_mdb + kind: Number + default_value: 0 + parameters: + min_value: 0 + max_value: 30000 + optional: false + description: Attenuation the pad is set to, in millidecibels. 5.0 dB is 5000. + order_weight: 1900 + - name: attenuation_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if attenuation_mdb__value is not none %}{{ attenuation_mdb__value + / 1000 }} dB{% endif %}' + optional: true + description: Attenuation in dB. Read-only, derived from attenuation_mdb. + order_weight: 1910 + - name: max_attenuation_mdb + kind: Number + parameters: + min_value: 0 + max_value: 30000 + optional: false + description: Largest attenuation this device can be dialled to, in millidecibels. + order_weight: 1920 + - name: max_attenuation_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if max_attenuation_mdb__value is not none %}{{ max_attenuation_mdb__value + / 1000 }} dB{% endif %}' + optional: true + description: Maximum attenuation in dB. Read-only, derived from max_attenuation_mdb. + order_weight: 1930 +- name: OpticalCarrier + namespace: Otn + description: One provisioned wavelength. Occupies its channel on every section it + crosses. + label: Optical carrier + icon: mdi:waves + include_in_menu: true + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: ^[A-Za-z0-9._-]+$ + optional: false + order_weight: 1000 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1100 + - name: status + kind: Dropdown + default_value: active + choices: + - name: planned + label: Planned + color: '#2196f3' + - name: active + label: Active + color: '#4caf50' + - name: decommissioned + label: Decommissioned + color: '#9e9e9e' + optional: false + order_weight: 1200 + relationships: + - name: channel + peer: OtnFrequencyGrid + kind: Attribute + cardinality: one + optional: false + identifier: otn_carrier__channel + order_weight: 800 + - name: optical_mode + peer: OtnOpticalMode + kind: Attribute + cardinality: one + optional: true + identifier: otn_carrier__optical_mode + order_weight: 810 + - name: optical_path + peer: OtnOpticalPath + kind: Attribute + cardinality: many + optional: true + identifier: otn_carrier__optical_path + order_weight: 820 + - name: sections + peer: OtnOpticalMultiplexSection + kind: Attribute + cardinality: many + optional: true + identifier: otn_carrier__sections + order_weight: 900 + - name: containers + peer: OtnContainer + kind: Attribute + cardinality: many + optional: true + identifier: otn_carrier__containers + order_weight: 910 + - name: odu_switches + peer: OtnOduSwitch + label: ODU switches this carrier is patched to + kind: Attribute + cardinality: many + optional: true + identifier: otn_odu_switch__carriers + order_weight: 920 + - name: line_ports + peer: OtnLinePort + label: Line ports terminating this wavelength + kind: Attribute + cardinality: many + optional: true + identifier: otn_carrier__line_ports + order_weight: 930 +- name: Service + namespace: Otn + description: Customer intent. Two endpoints, a rate, a profile and an optional latency + budget. + label: Service + icon: mdi:file-document-outline + include_in_menu: true + inherit_from: + - CoreArtifactTarget + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: ^[A-Za-z0-9._-]+$ + optional: false + order_weight: 1000 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1100 + - name: rate_gbps + kind: Number + parameters: + min_value: 1 + max_value: 1600 + optional: false + description: Requested client capacity in Gbps. Already whole in its natural unit. + order_weight: 1300 + - name: sla + kind: Dropdown + default_value: silver + choices: + - name: gold + label: Gold + color: '#ffc107' + - name: silver + label: Silver + color: '#9e9e9e' + - name: bronze + label: Bronze + color: '#795548' + - name: best_effort + label: Best effort + color: '#607d8b' + optional: false + order_weight: 1400 + - name: status + kind: Dropdown + default_value: planned + choices: + - name: planned + label: Planned + color: '#2196f3' + - name: provisioning + label: Provisioning + color: '#ff9800' + - name: active + label: Active + color: '#4caf50' + - name: rejected + label: Rejected + color: '#f44336' + - name: decommissioned + label: Decommissioned + color: '#9e9e9e' + optional: false + order_weight: 1500 + - name: service_profile + kind: Dropdown + default_value: ip-transit + choices: + - name: ai-training-dci + label: AI training DCI + color: '#e91e63' + - name: ai-inference + label: AI inference + color: '#9c27b0' + - name: hpc-research + label: HPC research + color: '#3f51b5' + - name: ip-transit + label: IP transit + color: '#2196f3' + - name: legacy-sdh + label: Legacy SDH + color: '#795548' + optional: false + order_weight: 1600 + - name: max_latency_ns + kind: Number + parameters: + min_value: 0 + max_value: 1000000000 + optional: true + description: One-way latency budget in nanoseconds. Null on the profiles that + have none. + order_weight: 1700 + - name: max_latency_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if max_latency_ns__value is not none %}{{ max_latency_ns__value + / 1000 }} us{% endif %}' + optional: true + description: Latency budget in microseconds. Read-only, derived from max_latency_ns. + order_weight: 1710 + relationships: + - name: endpoint_a + peer: OtnGenericDevice + kind: Attribute + cardinality: one + optional: false + identifier: otn_service__endpoint_a + order_weight: 800 + - name: endpoint_z + peer: OtnGenericDevice + kind: Attribute + cardinality: one + optional: false + identifier: otn_service__endpoint_z + order_weight: 810 + - name: optical_path + peer: OtnOpticalPath + kind: Component + cardinality: many + optional: true + identifier: otn_service__optical_path + on_delete: cascade + order_weight: 900 + - name: client_signal + peer: OtnClientSignal + kind: Attribute + cardinality: one + optional: true + identifier: otn_service__client_signal + order_weight: 910 + - name: diversity_group + peer: OtnDiversityGroup + kind: Attribute + cardinality: one + optional: true + identifier: otn_diversity_group__services + order_weight: 915 + - name: containers + peer: OtnContainer + kind: Attribute + cardinality: many + optional: true + identifier: otn_service__containers + order_weight: 920 + - name: customer + peer: OrganizationGeneric + label: Customer + kind: Attribute + cardinality: one + optional: false + identifier: otn_service__customer + order_weight: 1200 +- name: DiversityGroup + namespace: Otn + description: A declared diversity requirement. The services in one group must route + over disjoint conduits. + label: Diversity group + icon: mdi:call-split + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: ^[A-Za-z0-9._-]+$ + optional: false + order_weight: 1000 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1100 + relationships: + - name: services + peer: OtnService + kind: Attribute + cardinality: many + optional: true + identifier: otn_diversity_group__services + order_weight: 800 +- name: OpticalPath + namespace: Otn + description: The materialised chosen route for one service. + label: Optical path + icon: mdi:map-marker-path + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + uniqueness_constraints: + - - service + - segment_sequence__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 64 + regex: ^[A-Za-z0-9._-]+$ + optional: false + order_weight: 1000 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1100 + - name: segment_sequence + kind: Number + default_value: 1 + parameters: + min_value: 1 + max_value: 50 + optional: false + description: Which segment of its circuit this path is. 1 for a circuit that spans + one wavelength. + order_weight: 1200 + relationships: + - name: service + peer: OtnService + kind: Parent + cardinality: one + optional: false + identifier: otn_service__optical_path + order_weight: 800 + - name: carrier + peer: OtnOpticalCarrier + kind: Attribute + cardinality: one + optional: true + identifier: otn_carrier__optical_path + order_weight: 810 + - name: hops + peer: OtnPathHop + kind: Component + cardinality: many + optional: true + identifier: otn_path__hops + on_delete: cascade + order_weight: 900 +- name: PathHop + namespace: Otn + description: One ordered element on a path. + label: Path hop + icon: mdi:ray-vertex + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - sequence__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + parameters: + max_length: 96 + regex: ^[A-Za-z0-9._-]+$ + optional: false + order_weight: 1000 + - name: sequence + kind: Number + parameters: + min_value: 1 + max_value: 1000 + optional: false + description: Position along the path, counting from the endpoint A ROADM. + order_weight: 1100 + relationships: + - name: path + peer: OtnOpticalPath + kind: Parent + cardinality: one + optional: false + identifier: otn_path__hops + order_weight: 800 + - name: element + peer: OtnOpticalElement + kind: Attribute + cardinality: one + optional: false + identifier: otn_hop__element + order_weight: 810 +- name: Facility + namespace: Otn + description: A supercomputing facility hosted at a PoP. + label: Facility + icon: ri:cpu-line + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: name__value + attributes: + - name: name + kind: Text + unique: true + order_weight: 1000 + - name: description + kind: Text + optional: true + order_weight: 1200 + relationships: + - name: site + peer: LocationSite + kind: Attribute + cardinality: one + optional: true + identifier: otn_site__facility + order_weight: 900 +extensions: + nodes: + - kind: LocationSite + attributes: + - name: latitude_microdeg + kind: Number + parameters: + min_value: -90000000 + max_value: 90000000 + optional: true + description: Latitude in millionths of a degree. No Float kind exists. + order_weight: 1400 + - name: longitude_microdeg + kind: Number + parameters: + min_value: -180000000 + max_value: 180000000 + optional: true + description: Longitude in millionths of a degree. No Float kind exists. + order_weight: 1500 + - name: site_type + kind: Dropdown + default_value: pop + choices: + - name: pop + label: PoP + color: '#2196f3' + - name: customer + label: Customer site + color: '#ff9800' + optional: false + description: A PoP hosting OTN equipment, or a customer site handing traffic + to one. + order_weight: 1600 + relationships: + - name: otn_facility + peer: OtnFacility + kind: Attribute + cardinality: one + optional: true + identifier: otn_site__facility + order_weight: 930 + - name: devices + peer: OtnGenericDevice + kind: Generic + cardinality: many + optional: true + identifier: otn_site__devices + order_weight: 900 + - name: spans_a + peer: OtnFiberSpan + label: Fiber spans (A end) + kind: Attribute + cardinality: many + optional: true + identifier: otn_span__site_a + order_weight: 910 + - name: spans_b + peer: OtnFiberSpan + label: Fiber spans (B end) + kind: Attribute + cardinality: many + optional: true + identifier: otn_span__site_b + order_weight: 920 + +``` \ No newline at end of file From a89d5e8e3c02d40cd4a0bed36cc95dc9ea47defc Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 13:49:53 +0200 Subject: [PATCH 18/31] docs(otn): say what the monitoring exclusion actually excludes --- .metadata.yml | 7 ++++--- docs/docs/reference/otn.mdx | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.metadata.yml b/.metadata.yml index b216ecb1..2d8ef236 100644 --- a/.metadata.yml +++ b/.metadata.yml @@ -237,9 +237,10 @@ extensions/otn: ships no checks; the schema comments mark each such rule. - Rack elevations, device types and platforms for optical gear. Model the physical asset as a DcimPhysicalDevice and link it with OtnGenericDevice.dcim_device. - - Optical performance monitoring. No kind records optical power, OSNR, pre-FEC - BER or any other reading. That data changes continuously and belongs in the - network management system or a time series store, not in a source of truth. + - Optical performance monitoring. The schema carries ratings and settings, such + as a port's transmit power or a mode's required OSNR, but nothing that is measured, + such as live power, OSNR, pre-FEC BER or an error counter. Those change continuously + and belong in the network management system or a time series store. - Optical path budgets. OtnOpticalPath records which elements the light crosses and in what order, not the loss, OSNR margin or latency of that route. Compute those in a planning tool such as GNPy. diff --git a/docs/docs/reference/otn.mdx b/docs/docs/reference/otn.mdx index 0aac424d..4da07ca9 100644 --- a/docs/docs/reference/otn.mdx +++ b/docs/docs/reference/otn.mdx @@ -22,7 +22,7 @@ Out of scope, and what to reach for instead: - Cabling between optical devices. No OTN port is a DcimEndpoint, so extensions/cable cannot terminate on optical gear. Fiber is modelled as OtnFiberSpan and OtnConduit instead. - Validation of what a schema cannot express, such as which port kind may hold a pluggable, or whether a mux client port binds exactly one channel. This extension ships no checks; the schema comments mark each such rule. - Rack elevations, device types and platforms for optical gear. Model the physical asset as a DcimPhysicalDevice and link it with OtnGenericDevice.dcim_device. -- Optical performance monitoring. No kind records optical power, OSNR, pre-FEC BER or any other reading. That data changes continuously and belongs in the network management system or a time series store, not in a source of truth. +- Optical performance monitoring. The schema carries ratings and settings, such as a port's transmit power or a mode's required OSNR, but nothing that is measured, such as live power, OSNR, pre-FEC BER or an error counter. Those change continuously and belong in the network management system or a time series store. - Optical path budgets. OtnOpticalPath records which elements the light crosses and in what order, not the loss, OSNR margin or latency of that route. Compute those in a planning tool such as GNPy. ## Nodes From 6455cf06001a7b7a8e1693ec78dd35ac494de4f8 Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 14:47:20 +0200 Subject: [PATCH 19/31] fix(otn)!: rename the site devices relationship and soften site_type LocationSite already inherits a devices relationship from LocationHosting, peering DcimPhysicalDevice, so adding a second one named devices would have put two GraphQL fields of the same name on one node. It is renamed otn_devices, matching the otn_facility rename beside it, and keeps its identifier so the forward side is unchanged. site_type is no longer mandatory. LocationSite is shared across this library, and a mandatory OTN field would have declared every site in an estate a PoP. The block's attribute order weights move to 1600, 1700 and 1800, clear of the weights LocationSite already carries, and its relationships are listed in ascending weight order. BREAKING CHANGE: LocationSite.devices added by extensions/otn is now LocationSite.otn_devices. --- docs/docs/reference/otn.mdx | 30 +++++++++++++++--------------- extensions/otn/otn.yml | 28 +++++++++++++++------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/docs/docs/reference/otn.mdx b/docs/docs/reference/otn.mdx index 4da07ca9..b6adfe92 100644 --- a/docs/docs/reference/otn.mdx +++ b/docs/docs/reference/otn.mdx @@ -871,16 +871,16 @@ In this context "extensions" refer to modifications or additions to the existing | ---- | ----------- | ---- | -------- | ------------- | ------- | | latitude_microdeg | Latitude in millionths of a degree. No Float kind exists. | Number | True | | | | longitude_microdeg | Longitude in millionths of a degree. No Float kind exists. | Number | True | | | -| site_type | A PoP hosting OTN equipment, or a customer site handing traffic to one. | Dropdown | False | pop | pop, customer | +| site_type | A PoP hosting OTN equipment, or a customer site handing traffic to one. | Dropdown | True | pop | pop, customer | #### Relationships | name | peer | optional | cardinality | kind | | ---- | ---- | -------- | ----------- | ---- | -| otn_facility | OtnFacility | True | one | Attribute | -| devices | OtnGenericDevice | True | many | Generic | +| otn_devices | OtnGenericDevice | True | many | Generic | | spans_a | OtnFiberSpan | True | many | Attribute | | spans_b | OtnFiberSpan | True | many | Attribute | +| otn_facility | OtnFacility | True | one | Attribute | ## Code @@ -3472,7 +3472,7 @@ extensions: max_value: 90000000 optional: true description: Latitude in millionths of a degree. No Float kind exists. - order_weight: 1400 + order_weight: 1600 - name: longitude_microdeg kind: Number parameters: @@ -3480,7 +3480,7 @@ extensions: max_value: 180000000 optional: true description: Longitude in millionths of a degree. No Float kind exists. - order_weight: 1500 + order_weight: 1700 - name: site_type kind: Dropdown default_value: pop @@ -3491,19 +3491,12 @@ extensions: - name: customer label: Customer site color: '#ff9800' - optional: false + optional: true description: A PoP hosting OTN equipment, or a customer site handing traffic to one. - order_weight: 1600 + order_weight: 1800 relationships: - - name: otn_facility - peer: OtnFacility - kind: Attribute - cardinality: one - optional: true - identifier: otn_site__facility - order_weight: 930 - - name: devices + - name: otn_devices peer: OtnGenericDevice kind: Generic cardinality: many @@ -3526,5 +3519,12 @@ extensions: optional: true identifier: otn_span__site_b order_weight: 920 + - name: otn_facility + peer: OtnFacility + kind: Attribute + cardinality: one + optional: true + identifier: otn_site__facility + order_weight: 930 ``` \ No newline at end of file diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index 223a473a..b6e17e17 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -2780,7 +2780,9 @@ nodes: # LocationSite carries the OTN site fields rather than a second site kind. Its # `facility` relationship is renamed `otn_facility`, because LocationSite already -# has a `facility` Text attribute and a node cannot hold both under one name. +# has a `facility` Text attribute and a node cannot hold both under one name. Its +# `devices` relationship is renamed `otn_devices`, because LocationSite inherits a +# `devices` relationship from LocationHosting peering DcimPhysicalDevice. extensions: nodes: - kind: LocationSite @@ -2792,7 +2794,7 @@ extensions: max_value: 90000000 optional: true description: Latitude in millionths of a degree. No Float kind exists. - order_weight: 1400 + order_weight: 1600 - name: longitude_microdeg kind: Number parameters: @@ -2800,7 +2802,7 @@ extensions: max_value: 180000000 optional: true description: Longitude in millionths of a degree. No Float kind exists. - order_weight: 1500 + order_weight: 1700 - name: site_type kind: Dropdown default_value: pop @@ -2811,18 +2813,11 @@ extensions: - name: customer label: Customer site color: "#ff9800" - optional: false + optional: true description: A PoP hosting OTN equipment, or a customer site handing traffic to one. - order_weight: 1600 + order_weight: 1800 relationships: - - name: otn_facility - peer: OtnFacility - kind: Attribute - cardinality: one - optional: true - identifier: otn_site__facility - order_weight: 930 - - name: devices + - name: otn_devices peer: OtnGenericDevice kind: Generic cardinality: many @@ -2845,3 +2840,10 @@ extensions: optional: true identifier: otn_span__site_b order_weight: 920 + - name: otn_facility + peer: OtnFacility + kind: Attribute + cardinality: one + optional: true + identifier: otn_site__facility + order_weight: 930 From cfb7d6376eacdc9c4406c9cf9095bde655bdc768 Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 14:52:06 +0200 Subject: [PATCH 20/31] refactor(otn): declare connector_type once and close the port enums Six port kinds restated the connector_type Dropdown byte for byte from the generic they inherit it from, which is the hazard the generic's own comment warned about. The six remaining Text plus enum attributes become Dropdowns, so every closed vocabulary in the extension is a Dropdown with labels. element_class no longer describes an optical budget engine, which this library does not ship. --- docs/docs/reference/otn.mdx | 575 +++++++++++++----------------------- extensions/otn/otn.yml | 507 ++++++++++++------------------- 2 files changed, 405 insertions(+), 677 deletions(-) diff --git a/docs/docs/reference/otn.mdx b/docs/docs/reference/otn.mdx index b6adfe92..4e2550d9 100644 --- a/docs/docs/reference/otn.mdx +++ b/docs/docs/reference/otn.mdx @@ -35,12 +35,6 @@ Out of scope, and what to reach for instead: - **Icon:** mdi:ethernet - **Inherit From:** OtnGenericPort, OtnOpticalPort -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| connector_type | | Dropdown | True | | LC, SC, MPO-12, MPO-16, FC, E2000, MU, CS, splice | - #### Relationships | name | peer | optional | cardinality | kind | @@ -55,12 +49,6 @@ Out of scope, and what to reach for instead: - **Icon:** mdi:lan-connect - **Inherit From:** OtnGenericPort, OtnOpticalPort -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| connector_type | | Dropdown | True | | LC, SC, MPO-12, MPO-16, FC, E2000, MU, CS, splice | - #### Relationships | name | peer | optional | cardinality | kind | @@ -75,12 +63,6 @@ Out of scope, and what to reach for instead: - **Icon:** mdi:transit-connection - **Inherit From:** OtnGenericPort, OtnOpticalPort -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| connector_type | | Dropdown | True | | LC, SC, MPO-12, MPO-16, FC, E2000, MU, CS, splice | - #### Relationships | name | peer | optional | cardinality | kind | @@ -96,12 +78,6 @@ Out of scope, and what to reach for instead: - **Icon:** mdi:call-split - **Inherit From:** OtnGenericPort, OtnOpticalPort -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| connector_type | | Dropdown | True | | LC, SC, MPO-12, MPO-16, FC, E2000, MU, CS, splice | - ### RoadmDegreePort - **Label:** ROADM degree port @@ -110,12 +86,6 @@ Out of scope, and what to reach for instead: - **Icon:** mdi:compass-outline - **Inherit From:** OtnGenericPort, OtnOpticalPort -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| connector_type | | Dropdown | True | | LC, SC, MPO-12, MPO-16, FC, E2000, MU, CS, splice | - ### AmplifierPort - **Label:** Amplifier port @@ -124,12 +94,6 @@ Out of scope, and what to reach for instead: - **Icon:** mdi:amplifier - **Inherit From:** OtnGenericPort, OtnOpticalPort -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| connector_type | | Dropdown | True | | LC, SC, MPO-12, MPO-16, FC, E2000, MU, CS, splice | - ### MuxClientPort - **Label:** Mux client port @@ -367,7 +331,7 @@ Out of scope, and what to reach for instead: | ---- | ----------- | ---- | -------- | ------------- | ------- | | name | | Text | False | | | | mode_class | Two values, and the pluggable-reach report filters on it. | Dropdown | False | transponder | transponder, pluggable | -| modulation | | Text | False | | | +| modulation | | Dropdown | False | | DP-QPSK, DP-8QAM, DP-16QAM, DP-64QAM | | description | | Text | True | | | | line_rate_gbps | Client-side capacity in Gbps. Already a whole number in its natural unit. | Number | False | | | | baud_mbaud | Symbol rate in megabaud. 59.84 GBd is 59840. Sets the spectral width. | Number | False | | | @@ -376,7 +340,7 @@ Out of scope, and what to reach for instead: | cd_tolerance_fs_per_nm | Chromatic dispersion the receiver compensates, in fs/nm. 100000 ps/nm is 100000000. | Number | False | | | | nominal_reach_m | Vendor-quoted reach in metres. A starting point for the budget, not a guarantee. | Number | False | | | | nominal_reach_display | Nominal reach in km. Read-only, derived from nominal_reach_m. | Text | True | | | -| fec_type | | Text | False | SD-FEC | | +| fec_type | | Dropdown | False | SD-FEC | none, GFEC, SD-FEC, cFEC, oFEC | | fec_latency_ns | Encoder and decoder latency in nanoseconds. Small next to propagation, but not zero. | Number | False | 0 | | #### Relationships @@ -404,8 +368,8 @@ Out of scope, and what to reach for instead: | description | | Text | True | | | | bit_rate_kbps | Nominal line rate in kbps. E1 is 2048, 100GBASE-LR4 is 103100000. | Number | False | | | | bit_rate_display | Bit rate in Mbps below one gigabit, Gbps at or above it. | Text | True | | | -| default_container_type | The first step of the mapping chain, not the last. E1 maps into VC-12, not ODU1. | Text | False | | | -| default_mapping | ITU-T G.709 mapping procedure. Generic, bit-synchronous or asynchronous. | Text | False | | | +| default_container_type | The first step of the mapping chain, not the last. E1 maps into VC-12, not ODU1. | Dropdown | False | | ODU0, ODU1, ODU2, ODU2e, ODU3, ODU4, ODUC1, ODUC2, ODUC3, ODUC4, ODUflex, VC-12, VC-4, STM-N | +| default_mapping | ITU-T G.709 mapping procedure. Generic, bit-synchronous or asynchronous. | Dropdown | False | | GMP, BMP, AMP | #### Relationships @@ -427,8 +391,8 @@ Out of scope, and what to reach for instead: | name | description | kind | optional | default_value | choices | | ---- | ----------- | ---- | -------- | ------------- | ------- | | name | | Text | False | | | -| odu_type | | Text | False | | | -| mapping_mode | | Text | False | GMP | | +| odu_type | | Dropdown | False | | ODU0, ODU1, ODU2, ODU2e, ODU3, ODU4, ODUC1, ODUC2, ODUC3, ODUC4, ODUC6, ODUC8, ODUflex, VC-12, VC-4, STM-N | +| mapping_mode | | Dropdown | False | GMP | GMP, BMP, AMP | | description | | Text | True | | | | segment_sequence | Which segment of its circuit this container rides. 1 for a circuit that spans one wavelength. | Number | False | 1 | | | tributary_slots | Slots this container occupies in its parent. An ODU2 in an ODU4 takes 8. | Number | False | 0 | | @@ -794,7 +758,7 @@ Out of scope, and what to reach for instead: | insertion_loss_display | Insertion loss in dB. Read-only, derived from insertion_loss_mdb. | Text | True | | | | vendor | | Text | True | | | | model | | Text | True | | | -| element_class | What the optical budget engine branches on. | Dropdown | False | | transponder, roadm, amplifier, mux_demux, patch_panel, fiber_span, splitter, attenuator, raman_pump, odu_switch | +| element_class | Which class of optical element this is. Set it to match the kind. | Dropdown | False | | transponder, roadm, amplifier, mux_demux, patch_panel, fiber_span, splitter, attenuator, raman_pump, odu_switch | ### GenericDevice @@ -1075,7 +1039,7 @@ generics: label: ODU switch color: '#673ab7' optional: false - description: What the optical budget engine branches on. + description: Which class of optical element this is. Set it to match the kind. order_weight: 1800 - name: GenericDevice namespace: Otn @@ -1338,50 +1302,6 @@ nodes: inherit_from: - OtnGenericPort - OtnOpticalPort - attributes: - - name: connector_type - kind: Dropdown - choices: - - name: LC - label: LC - description: Small-form duplex latch. What transponder and pluggable optics - take. - color: '#2196f3' - - name: SC - label: SC - description: Push-pull square ferrule, common on older distribution frames. - color: '#4caf50' - - name: MPO-12 - label: MPO-12 - description: Twelve-fibre ribbon ferrule for parallel optics. - color: '#9c27b0' - - name: MPO-16 - label: MPO-16 - description: Sixteen-fibre ribbon ferrule for 400G parallel optics. - color: '#673ab7' - - name: FC - label: FC - description: Threaded ferrule. Still found on test sets and metro equipment. - color: '#ff9800' - - name: E2000 - label: E2000 - description: Shuttered push-pull connector, widespread on European carrier plant. - color: '#00bcd4' - - name: MU - label: MU - description: Miniature unibody connector, half the footprint of an SC. - color: '#8bc34a' - - name: CS - label: CS - description: Narrow-pitch duplex connector, used for QSFP-DD breakout. - color: '#e91e63' - - name: splice - label: Splice - description: Fusion splice rather than a mated pair. No connector loss and no - polish. - color: '#9e9e9e' - optional: true - order_weight: 1900 relationships: - name: transceiver peer: OtnTransceiver @@ -1400,50 +1320,6 @@ nodes: inherit_from: - OtnGenericPort - OtnOpticalPort - attributes: - - name: connector_type - kind: Dropdown - choices: - - name: LC - label: LC - description: Small-form duplex latch. What transponder and pluggable optics - take. - color: '#2196f3' - - name: SC - label: SC - description: Push-pull square ferrule, common on older distribution frames. - color: '#4caf50' - - name: MPO-12 - label: MPO-12 - description: Twelve-fibre ribbon ferrule for parallel optics. - color: '#9c27b0' - - name: MPO-16 - label: MPO-16 - description: Sixteen-fibre ribbon ferrule for 400G parallel optics. - color: '#673ab7' - - name: FC - label: FC - description: Threaded ferrule. Still found on test sets and metro equipment. - color: '#ff9800' - - name: E2000 - label: E2000 - description: Shuttered push-pull connector, widespread on European carrier plant. - color: '#00bcd4' - - name: MU - label: MU - description: Miniature unibody connector, half the footprint of an SC. - color: '#8bc34a' - - name: CS - label: CS - description: Narrow-pitch duplex connector, used for QSFP-DD breakout. - color: '#e91e63' - - name: splice - label: Splice - description: Fusion splice rather than a mated pair. No connector loss and no - polish. - color: '#9e9e9e' - optional: true - order_weight: 1900 relationships: - name: transceiver peer: OtnTransceiver @@ -1462,50 +1338,6 @@ nodes: inherit_from: - OtnGenericPort - OtnOpticalPort - attributes: - - name: connector_type - kind: Dropdown - choices: - - name: LC - label: LC - description: Small-form duplex latch. What transponder and pluggable optics - take. - color: '#2196f3' - - name: SC - label: SC - description: Push-pull square ferrule, common on older distribution frames. - color: '#4caf50' - - name: MPO-12 - label: MPO-12 - description: Twelve-fibre ribbon ferrule for parallel optics. - color: '#9c27b0' - - name: MPO-16 - label: MPO-16 - description: Sixteen-fibre ribbon ferrule for 400G parallel optics. - color: '#673ab7' - - name: FC - label: FC - description: Threaded ferrule. Still found on test sets and metro equipment. - color: '#ff9800' - - name: E2000 - label: E2000 - description: Shuttered push-pull connector, widespread on European carrier plant. - color: '#00bcd4' - - name: MU - label: MU - description: Miniature unibody connector, half the footprint of an SC. - color: '#8bc34a' - - name: CS - label: CS - description: Narrow-pitch duplex connector, used for QSFP-DD breakout. - color: '#e91e63' - - name: splice - label: Splice - description: Fusion splice rather than a mated pair. No connector loss and no - polish. - color: '#9e9e9e' - optional: true - order_weight: 1900 relationships: - name: carrier peer: OtnOpticalCarrier @@ -1532,50 +1364,6 @@ nodes: inherit_from: - OtnGenericPort - OtnOpticalPort - attributes: - - name: connector_type - kind: Dropdown - choices: - - name: LC - label: LC - description: Small-form duplex latch. What transponder and pluggable optics - take. - color: '#2196f3' - - name: SC - label: SC - description: Push-pull square ferrule, common on older distribution frames. - color: '#4caf50' - - name: MPO-12 - label: MPO-12 - description: Twelve-fibre ribbon ferrule for parallel optics. - color: '#9c27b0' - - name: MPO-16 - label: MPO-16 - description: Sixteen-fibre ribbon ferrule for 400G parallel optics. - color: '#673ab7' - - name: FC - label: FC - description: Threaded ferrule. Still found on test sets and metro equipment. - color: '#ff9800' - - name: E2000 - label: E2000 - description: Shuttered push-pull connector, widespread on European carrier plant. - color: '#00bcd4' - - name: MU - label: MU - description: Miniature unibody connector, half the footprint of an SC. - color: '#8bc34a' - - name: CS - label: CS - description: Narrow-pitch duplex connector, used for QSFP-DD breakout. - color: '#e91e63' - - name: splice - label: Splice - description: Fusion splice rather than a mated pair. No connector loss and no - polish. - color: '#9e9e9e' - optional: true - order_weight: 1900 - name: RoadmDegreePort namespace: Otn description: Line-facing degree on a ROADM. One per direction. @@ -1585,50 +1373,6 @@ nodes: inherit_from: - OtnGenericPort - OtnOpticalPort - attributes: - - name: connector_type - kind: Dropdown - choices: - - name: LC - label: LC - description: Small-form duplex latch. What transponder and pluggable optics - take. - color: '#2196f3' - - name: SC - label: SC - description: Push-pull square ferrule, common on older distribution frames. - color: '#4caf50' - - name: MPO-12 - label: MPO-12 - description: Twelve-fibre ribbon ferrule for parallel optics. - color: '#9c27b0' - - name: MPO-16 - label: MPO-16 - description: Sixteen-fibre ribbon ferrule for 400G parallel optics. - color: '#673ab7' - - name: FC - label: FC - description: Threaded ferrule. Still found on test sets and metro equipment. - color: '#ff9800' - - name: E2000 - label: E2000 - description: Shuttered push-pull connector, widespread on European carrier plant. - color: '#00bcd4' - - name: MU - label: MU - description: Miniature unibody connector, half the footprint of an SC. - color: '#8bc34a' - - name: CS - label: CS - description: Narrow-pitch duplex connector, used for QSFP-DD breakout. - color: '#e91e63' - - name: splice - label: Splice - description: Fusion splice rather than a mated pair. No connector loss and no - polish. - color: '#9e9e9e' - optional: true - order_weight: 1900 - name: AmplifierPort namespace: Otn description: Amplifier input or output. @@ -1638,50 +1382,6 @@ nodes: inherit_from: - OtnGenericPort - OtnOpticalPort - attributes: - - name: connector_type - kind: Dropdown - choices: - - name: LC - label: LC - description: Small-form duplex latch. What transponder and pluggable optics - take. - color: '#2196f3' - - name: SC - label: SC - description: Push-pull square ferrule, common on older distribution frames. - color: '#4caf50' - - name: MPO-12 - label: MPO-12 - description: Twelve-fibre ribbon ferrule for parallel optics. - color: '#9c27b0' - - name: MPO-16 - label: MPO-16 - description: Sixteen-fibre ribbon ferrule for 400G parallel optics. - color: '#673ab7' - - name: FC - label: FC - description: Threaded ferrule. Still found on test sets and metro equipment. - color: '#ff9800' - - name: E2000 - label: E2000 - description: Shuttered push-pull connector, widespread on European carrier plant. - color: '#00bcd4' - - name: MU - label: MU - description: Miniature unibody connector, half the footprint of an SC. - color: '#8bc34a' - - name: CS - label: CS - description: Narrow-pitch duplex connector, used for QSFP-DD breakout. - color: '#e91e63' - - name: splice - label: Splice - description: Fusion splice rather than a mated pair. No connector loss and no - polish. - color: '#9e9e9e' - optional: true - order_weight: 1900 - name: MuxClientPort namespace: Otn description: One channel of a multiplexer. The side facing the transponder or router @@ -2336,12 +2036,25 @@ nodes: description: Two values, and the pluggable-reach report filters on it. order_weight: 1100 - name: modulation - kind: Text - enum: - - DP-QPSK - - DP-8QAM - - DP-16QAM - - DP-64QAM + kind: Dropdown + choices: + - name: DP-QPSK + label: DP-QPSK + description: Dual-polarisation QPSK. Four bits per symbol, the longest reach + of the four. + color: '#2196f3' + - name: DP-8QAM + label: DP-8QAM + description: Dual-polarisation 8QAM. Six bits per symbol. + color: '#4caf50' + - name: DP-16QAM + label: DP-16QAM + description: Dual-polarisation 16QAM. Eight bits per symbol. + color: '#ff9800' + - name: DP-64QAM + label: DP-64QAM + description: Dual-polarisation 64QAM. Twelve bits per symbol, the shortest reach. + color: '#e91e63' optional: false order_weight: 1200 - name: description @@ -2414,14 +2127,32 @@ nodes: description: Nominal reach in km. Read-only, derived from nominal_reach_m. order_weight: 1910 - name: fec_type - kind: Text + kind: Dropdown default_value: SD-FEC - enum: - - none - - GFEC - - SD-FEC - - cFEC - - oFEC + choices: + - name: none + label: None + description: No forward error correction. + color: '#9e9e9e' + - name: GFEC + label: GFEC + description: The G.709 Reed-Solomon code. Hard decision, about 7 percent overhead. + color: '#2196f3' + - name: SD-FEC + label: SD-FEC + description: Soft-decision FEC. More coding gain than a hard-decision code, + at more latency. + color: '#4caf50' + - name: cFEC + label: cFEC + description: Concatenated FEC from OIF 400ZR. Less coding gain and less power + than oFEC. + color: '#00bcd4' + - name: oFEC + label: oFEC + description: Open FEC from OpenROADM and OpenZR+. More coding gain, for longer + reach. + color: '#9c27b0' optional: false order_weight: 1950 - name: fec_latency_ns @@ -2523,32 +2254,86 @@ nodes: description: Bit rate in Mbps below one gigabit, Gbps at or above it. order_weight: 1510 - name: default_container_type - kind: Text - enum: - - ODU0 - - ODU1 - - ODU2 - - ODU2e - - ODU3 - - ODU4 - - ODUC1 - - ODUC2 - - ODUC3 - - ODUC4 - - ODUflex - - VC-12 - - VC-4 - - STM-N + kind: Dropdown + choices: + - name: ODU0 + label: ODU0 + description: About 1.25 Gbps. Sized for a gigabit Ethernet client. + color: '#2196f3' + - name: ODU1 + label: ODU1 + description: About 2.5 Gbps, the original G.709 tributary rate. + color: '#2196f3' + - name: ODU2 + label: ODU2 + description: About 10 Gbps. Takes 10G Ethernet at its WAN rate. + color: '#2196f3' + - name: ODU2e + label: ODU2e + description: About 10.4 Gbps, sized for the 10GBASE-R LAN rate. + color: '#2196f3' + - name: ODU3 + label: ODU3 + description: About 40 Gbps. + color: '#2196f3' + - name: ODU4 + label: ODU4 + description: About 100 Gbps. Takes 100G Ethernet. + color: '#2196f3' + - name: ODUC1 + label: ODUC1 + description: ODUCn at n=1. About 100 Gbps. + color: '#673ab7' + - name: ODUC2 + label: ODUC2 + description: ODUCn at n=2. About 200 Gbps. + color: '#673ab7' + - name: ODUC3 + label: ODUC3 + description: ODUCn at n=3. About 300 Gbps. + color: '#673ab7' + - name: ODUC4 + label: ODUC4 + description: ODUCn at n=4. About 400 Gbps. + color: '#673ab7' + - name: ODUflex + label: ODUflex + description: Sized to the client rather than to a fixed rate. + color: '#00bcd4' + - name: VC-12 + label: VC-12 + description: SDH virtual container carrying a 2 Mbps E1. + color: '#ff9800' + - name: VC-4 + label: VC-4 + description: SDH virtual container carrying the 140 Mbps STM-1 payload. + color: '#ff9800' + - name: STM-N + label: STM-N + description: A whole SDH aggregate carried transparently. + color: '#ff9800' optional: false description: The first step of the mapping chain, not the last. E1 maps into VC-12, not ODU1. order_weight: 1600 - name: default_mapping - kind: Text - enum: - - GMP - - BMP - - AMP + kind: Dropdown + choices: + - name: GMP + label: GMP + description: Generic mapping procedure. Rate agnostic, and what a modern client + uses. + color: '#2196f3' + - name: BMP + label: BMP + description: Bit-synchronous mapping procedure. Client and container clocks + are locked. + color: '#4caf50' + - name: AMP + label: AMP + description: Asynchronous mapping procedure. Justification bytes absorb the + clock offset. + color: '#ff9800' optional: false description: ITU-T G.709 mapping procedure. Generic, bit-synchronous or asynchronous. order_weight: 1700 @@ -2589,33 +2374,93 @@ nodes: optional: false order_weight: 1000 - name: odu_type - kind: Text - enum: - - ODU0 - - ODU1 - - ODU2 - - ODU2e - - ODU3 - - ODU4 - - ODUC1 - - ODUC2 - - ODUC3 - - ODUC4 - - ODUC6 - - ODUC8 - - ODUflex - - VC-12 - - VC-4 - - STM-N + kind: Dropdown + choices: + - name: ODU0 + label: ODU0 + description: About 1.25 Gbps. Sized for a gigabit Ethernet client. + color: '#2196f3' + - name: ODU1 + label: ODU1 + description: About 2.5 Gbps, the original G.709 tributary rate. + color: '#2196f3' + - name: ODU2 + label: ODU2 + description: About 10 Gbps. Takes 10G Ethernet at its WAN rate. + color: '#2196f3' + - name: ODU2e + label: ODU2e + description: About 10.4 Gbps, sized for the 10GBASE-R LAN rate. + color: '#2196f3' + - name: ODU3 + label: ODU3 + description: About 40 Gbps. + color: '#2196f3' + - name: ODU4 + label: ODU4 + description: About 100 Gbps. Takes 100G Ethernet. + color: '#2196f3' + - name: ODUC1 + label: ODUC1 + description: ODUCn at n=1. About 100 Gbps. + color: '#673ab7' + - name: ODUC2 + label: ODUC2 + description: ODUCn at n=2. About 200 Gbps. + color: '#673ab7' + - name: ODUC3 + label: ODUC3 + description: ODUCn at n=3. About 300 Gbps. + color: '#673ab7' + - name: ODUC4 + label: ODUC4 + description: ODUCn at n=4. About 400 Gbps. + color: '#673ab7' + - name: ODUC6 + label: ODUC6 + description: ODUCn at n=6. About 600 Gbps. + color: '#673ab7' + - name: ODUC8 + label: ODUC8 + description: ODUCn at n=8. About 800 Gbps. + color: '#673ab7' + - name: ODUflex + label: ODUflex + description: Sized to the client rather than to a fixed rate. + color: '#00bcd4' + - name: VC-12 + label: VC-12 + description: SDH virtual container carrying a 2 Mbps E1. + color: '#ff9800' + - name: VC-4 + label: VC-4 + description: SDH virtual container carrying the 140 Mbps STM-1 payload. + color: '#ff9800' + - name: STM-N + label: STM-N + description: A whole SDH aggregate carried transparently. + color: '#ff9800' optional: false order_weight: 1100 - name: mapping_mode - kind: Text + kind: Dropdown default_value: GMP - enum: - - GMP - - BMP - - AMP + choices: + - name: GMP + label: GMP + description: Generic mapping procedure. Rate agnostic, and what a modern client + uses. + color: '#2196f3' + - name: BMP + label: BMP + description: Bit-synchronous mapping procedure. Client and container clocks + are locked. + color: '#4caf50' + - name: AMP + label: AMP + description: Asynchronous mapping procedure. Justification bytes absorb the + clock offset. + color: '#ff9800' optional: false order_weight: 1200 - name: description diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index b6e17e17..286d7490 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -209,7 +209,7 @@ generics: label: ODU switch color: "#673ab7" optional: false - description: What the optical budget engine branches on. + description: Which class of optical element this is. Set it to match the kind. order_weight: 1800 - name: GenericDevice @@ -366,8 +366,8 @@ generics: description: Receiver sensitivity in dBm. Derived from rx_sensitivity_mdbm. order_weight: 1810 # A Dropdown rather than an enum, so a connector carries a label, a colour and - # a sentence. Every kind inheriting this generic restates the list, and a - # restatement one choice short is accepted in silence. + # a sentence. The list is declared once here, and every kind inheriting this + # generic gets it. - name: connector_type kind: Dropdown choices: @@ -490,48 +490,6 @@ nodes: inherit_from: - OtnGenericPort - OtnOpticalPort - attributes: - - name: connector_type - kind: Dropdown - choices: - - name: LC - label: LC - description: Small-form duplex latch. What transponder and pluggable optics take. - color: "#2196f3" - - name: SC - label: SC - description: Push-pull square ferrule, common on older distribution frames. - color: "#4caf50" - - name: MPO-12 - label: MPO-12 - description: Twelve-fibre ribbon ferrule for parallel optics. - color: "#9c27b0" - - name: MPO-16 - label: MPO-16 - description: Sixteen-fibre ribbon ferrule for 400G parallel optics. - color: "#673ab7" - - name: FC - label: FC - description: Threaded ferrule. Still found on test sets and metro equipment. - color: "#ff9800" - - name: E2000 - label: E2000 - description: Shuttered push-pull connector, widespread on European carrier plant. - color: "#00bcd4" - - name: MU - label: MU - description: Miniature unibody connector, half the footprint of an SC. - color: "#8bc34a" - - name: CS - label: CS - description: Narrow-pitch duplex connector, used for QSFP-DD breakout. - color: "#e91e63" - - name: splice - label: Splice - description: Fusion splice rather than a mated pair. No connector loss and no polish. - color: "#9e9e9e" - optional: true - order_weight: 1900 relationships: # The module fitted in this port, and the inverse of OtnTransceiver.port. Both # ends are cardinality one, so a second module in one port is refused at write @@ -554,48 +512,6 @@ nodes: inherit_from: - OtnGenericPort - OtnOpticalPort - attributes: - - name: connector_type - kind: Dropdown - choices: - - name: LC - label: LC - description: Small-form duplex latch. What transponder and pluggable optics take. - color: "#2196f3" - - name: SC - label: SC - description: Push-pull square ferrule, common on older distribution frames. - color: "#4caf50" - - name: MPO-12 - label: MPO-12 - description: Twelve-fibre ribbon ferrule for parallel optics. - color: "#9c27b0" - - name: MPO-16 - label: MPO-16 - description: Sixteen-fibre ribbon ferrule for 400G parallel optics. - color: "#673ab7" - - name: FC - label: FC - description: Threaded ferrule. Still found on test sets and metro equipment. - color: "#ff9800" - - name: E2000 - label: E2000 - description: Shuttered push-pull connector, widespread on European carrier plant. - color: "#00bcd4" - - name: MU - label: MU - description: Miniature unibody connector, half the footprint of an SC. - color: "#8bc34a" - - name: CS - label: CS - description: Narrow-pitch duplex connector, used for QSFP-DD breakout. - color: "#e91e63" - - name: splice - label: Splice - description: Fusion splice rather than a mated pair. No connector loss and no polish. - color: "#9e9e9e" - optional: true - order_weight: 1900 relationships: - name: transceiver peer: OtnTransceiver @@ -615,48 +531,6 @@ nodes: inherit_from: - OtnGenericPort - OtnOpticalPort - attributes: - - name: connector_type - kind: Dropdown - choices: - - name: LC - label: LC - description: Small-form duplex latch. What transponder and pluggable optics take. - color: "#2196f3" - - name: SC - label: SC - description: Push-pull square ferrule, common on older distribution frames. - color: "#4caf50" - - name: MPO-12 - label: MPO-12 - description: Twelve-fibre ribbon ferrule for parallel optics. - color: "#9c27b0" - - name: MPO-16 - label: MPO-16 - description: Sixteen-fibre ribbon ferrule for 400G parallel optics. - color: "#673ab7" - - name: FC - label: FC - description: Threaded ferrule. Still found on test sets and metro equipment. - color: "#ff9800" - - name: E2000 - label: E2000 - description: Shuttered push-pull connector, widespread on European carrier plant. - color: "#00bcd4" - - name: MU - label: MU - description: Miniature unibody connector, half the footprint of an SC. - color: "#8bc34a" - - name: CS - label: CS - description: Narrow-pitch duplex connector, used for QSFP-DD breakout. - color: "#e91e63" - - name: splice - label: Splice - description: Fusion splice rather than a mated pair. No connector loss and no polish. - color: "#9e9e9e" - optional: true - order_weight: 1900 relationships: # The wavelength this port terminates. The deletion boundary stops here: a # carrier is a spectrum allocation on a route, terminated at both of its ends, @@ -688,48 +562,6 @@ nodes: inherit_from: - OtnGenericPort - OtnOpticalPort - attributes: - - name: connector_type - kind: Dropdown - choices: - - name: LC - label: LC - description: Small-form duplex latch. What transponder and pluggable optics take. - color: "#2196f3" - - name: SC - label: SC - description: Push-pull square ferrule, common on older distribution frames. - color: "#4caf50" - - name: MPO-12 - label: MPO-12 - description: Twelve-fibre ribbon ferrule for parallel optics. - color: "#9c27b0" - - name: MPO-16 - label: MPO-16 - description: Sixteen-fibre ribbon ferrule for 400G parallel optics. - color: "#673ab7" - - name: FC - label: FC - description: Threaded ferrule. Still found on test sets and metro equipment. - color: "#ff9800" - - name: E2000 - label: E2000 - description: Shuttered push-pull connector, widespread on European carrier plant. - color: "#00bcd4" - - name: MU - label: MU - description: Miniature unibody connector, half the footprint of an SC. - color: "#8bc34a" - - name: CS - label: CS - description: Narrow-pitch duplex connector, used for QSFP-DD breakout. - color: "#e91e63" - - name: splice - label: Splice - description: Fusion splice rather than a mated pair. No connector loss and no polish. - color: "#9e9e9e" - optional: true - order_weight: 1900 - name: RoadmDegreePort namespace: Otn @@ -740,48 +572,6 @@ nodes: inherit_from: - OtnGenericPort - OtnOpticalPort - attributes: - - name: connector_type - kind: Dropdown - choices: - - name: LC - label: LC - description: Small-form duplex latch. What transponder and pluggable optics take. - color: "#2196f3" - - name: SC - label: SC - description: Push-pull square ferrule, common on older distribution frames. - color: "#4caf50" - - name: MPO-12 - label: MPO-12 - description: Twelve-fibre ribbon ferrule for parallel optics. - color: "#9c27b0" - - name: MPO-16 - label: MPO-16 - description: Sixteen-fibre ribbon ferrule for 400G parallel optics. - color: "#673ab7" - - name: FC - label: FC - description: Threaded ferrule. Still found on test sets and metro equipment. - color: "#ff9800" - - name: E2000 - label: E2000 - description: Shuttered push-pull connector, widespread on European carrier plant. - color: "#00bcd4" - - name: MU - label: MU - description: Miniature unibody connector, half the footprint of an SC. - color: "#8bc34a" - - name: CS - label: CS - description: Narrow-pitch duplex connector, used for QSFP-DD breakout. - color: "#e91e63" - - name: splice - label: Splice - description: Fusion splice rather than a mated pair. No connector loss and no polish. - color: "#9e9e9e" - optional: true - order_weight: 1900 - name: AmplifierPort namespace: Otn @@ -792,48 +582,6 @@ nodes: inherit_from: - OtnGenericPort - OtnOpticalPort - attributes: - - name: connector_type - kind: Dropdown - choices: - - name: LC - label: LC - description: Small-form duplex latch. What transponder and pluggable optics take. - color: "#2196f3" - - name: SC - label: SC - description: Push-pull square ferrule, common on older distribution frames. - color: "#4caf50" - - name: MPO-12 - label: MPO-12 - description: Twelve-fibre ribbon ferrule for parallel optics. - color: "#9c27b0" - - name: MPO-16 - label: MPO-16 - description: Sixteen-fibre ribbon ferrule for 400G parallel optics. - color: "#673ab7" - - name: FC - label: FC - description: Threaded ferrule. Still found on test sets and metro equipment. - color: "#ff9800" - - name: E2000 - label: E2000 - description: Shuttered push-pull connector, widespread on European carrier plant. - color: "#00bcd4" - - name: MU - label: MU - description: Miniature unibody connector, half the footprint of an SC. - color: "#8bc34a" - - name: CS - label: CS - description: Narrow-pitch duplex connector, used for QSFP-DD breakout. - color: "#e91e63" - - name: splice - label: Splice - description: Fusion splice rather than a mated pair. No connector loss and no polish. - color: "#9e9e9e" - optional: true - order_weight: 1900 # One multiplexer channel and the common side it shares. Neither declares an # insertion loss: that belongs to the device, so a per-port figure would double @@ -1527,12 +1275,24 @@ nodes: description: Two values, and the pluggable-reach report filters on it. order_weight: 1100 - name: modulation - kind: Text - enum: - - DP-QPSK - - DP-8QAM - - DP-16QAM - - DP-64QAM + kind: Dropdown + choices: + - name: DP-QPSK + label: DP-QPSK + description: Dual-polarisation QPSK. Four bits per symbol, the longest reach of the four. + color: "#2196f3" + - name: DP-8QAM + label: DP-8QAM + description: Dual-polarisation 8QAM. Six bits per symbol. + color: "#4caf50" + - name: DP-16QAM + label: DP-16QAM + description: Dual-polarisation 16QAM. Eight bits per symbol. + color: "#ff9800" + - name: DP-64QAM + label: DP-64QAM + description: Dual-polarisation 64QAM. Twelve bits per symbol, the shortest reach. + color: "#e91e63" optional: false order_weight: 1200 - name: description @@ -1602,14 +1362,29 @@ nodes: description: Nominal reach in km. Read-only, derived from nominal_reach_m. order_weight: 1910 - name: fec_type - kind: Text + kind: Dropdown default_value: SD-FEC - enum: - - none - - GFEC - - SD-FEC - - cFEC - - oFEC + choices: + - name: none + label: None + description: No forward error correction. + color: "#9e9e9e" + - name: GFEC + label: GFEC + description: The G.709 Reed-Solomon code. Hard decision, about 7 percent overhead. + color: "#2196f3" + - name: SD-FEC + label: SD-FEC + description: Soft-decision FEC. More coding gain than a hard-decision code, at more latency. + color: "#4caf50" + - name: cFEC + label: cFEC + description: Concatenated FEC from OIF 400ZR. Less coding gain and less power than oFEC. + color: "#00bcd4" + - name: oFEC + label: oFEC + description: Open FEC from OpenROADM and OpenZR+. More coding gain, for longer reach. + color: "#9c27b0" optional: false order_weight: 1950 - name: fec_latency_ns @@ -1715,31 +1490,82 @@ nodes: description: Bit rate in Mbps below one gigabit, Gbps at or above it. order_weight: 1510 - name: default_container_type - kind: Text - enum: - - ODU0 - - ODU1 - - ODU2 - - ODU2e - - ODU3 - - ODU4 - - ODUC1 - - ODUC2 - - ODUC3 - - ODUC4 - - ODUflex - - VC-12 - - VC-4 - - STM-N + kind: Dropdown + choices: + - name: ODU0 + label: ODU0 + description: About 1.25 Gbps. Sized for a gigabit Ethernet client. + color: "#2196f3" + - name: ODU1 + label: ODU1 + description: About 2.5 Gbps, the original G.709 tributary rate. + color: "#2196f3" + - name: ODU2 + label: ODU2 + description: About 10 Gbps. Takes 10G Ethernet at its WAN rate. + color: "#2196f3" + - name: ODU2e + label: ODU2e + description: About 10.4 Gbps, sized for the 10GBASE-R LAN rate. + color: "#2196f3" + - name: ODU3 + label: ODU3 + description: About 40 Gbps. + color: "#2196f3" + - name: ODU4 + label: ODU4 + description: About 100 Gbps. Takes 100G Ethernet. + color: "#2196f3" + - name: ODUC1 + label: ODUC1 + description: ODUCn at n=1. About 100 Gbps. + color: "#673ab7" + - name: ODUC2 + label: ODUC2 + description: ODUCn at n=2. About 200 Gbps. + color: "#673ab7" + - name: ODUC3 + label: ODUC3 + description: ODUCn at n=3. About 300 Gbps. + color: "#673ab7" + - name: ODUC4 + label: ODUC4 + description: ODUCn at n=4. About 400 Gbps. + color: "#673ab7" + - name: ODUflex + label: ODUflex + description: Sized to the client rather than to a fixed rate. + color: "#00bcd4" + - name: VC-12 + label: VC-12 + description: SDH virtual container carrying a 2 Mbps E1. + color: "#ff9800" + - name: VC-4 + label: VC-4 + description: SDH virtual container carrying the 140 Mbps STM-1 payload. + color: "#ff9800" + - name: STM-N + label: STM-N + description: A whole SDH aggregate carried transparently. + color: "#ff9800" optional: false description: The first step of the mapping chain, not the last. E1 maps into VC-12, not ODU1. order_weight: 1600 - name: default_mapping - kind: Text - enum: - - GMP - - BMP - - AMP + kind: Dropdown + choices: + - name: GMP + label: GMP + description: Generic mapping procedure. Rate agnostic, and what a modern client uses. + color: "#2196f3" + - name: BMP + label: BMP + description: Bit-synchronous mapping procedure. Client and container clocks are locked. + color: "#4caf50" + - name: AMP + label: AMP + description: Asynchronous mapping procedure. Justification bytes absorb the clock offset. + color: "#ff9800" optional: false description: ITU-T G.709 mapping procedure. Generic, bit-synchronous or asynchronous. order_weight: 1700 @@ -1783,33 +1609,90 @@ nodes: # OtnClientSignal.default_container_type's fourteen values plus ODUC6 and # ODUC8, which are line containers no client maps directly into. - name: odu_type - kind: Text - enum: - - ODU0 - - ODU1 - - ODU2 - - ODU2e - - ODU3 - - ODU4 - - ODUC1 - - ODUC2 - - ODUC3 - - ODUC4 - - ODUC6 - - ODUC8 - - ODUflex - - VC-12 - - VC-4 - - STM-N + kind: Dropdown + choices: + - name: ODU0 + label: ODU0 + description: About 1.25 Gbps. Sized for a gigabit Ethernet client. + color: "#2196f3" + - name: ODU1 + label: ODU1 + description: About 2.5 Gbps, the original G.709 tributary rate. + color: "#2196f3" + - name: ODU2 + label: ODU2 + description: About 10 Gbps. Takes 10G Ethernet at its WAN rate. + color: "#2196f3" + - name: ODU2e + label: ODU2e + description: About 10.4 Gbps, sized for the 10GBASE-R LAN rate. + color: "#2196f3" + - name: ODU3 + label: ODU3 + description: About 40 Gbps. + color: "#2196f3" + - name: ODU4 + label: ODU4 + description: About 100 Gbps. Takes 100G Ethernet. + color: "#2196f3" + - name: ODUC1 + label: ODUC1 + description: ODUCn at n=1. About 100 Gbps. + color: "#673ab7" + - name: ODUC2 + label: ODUC2 + description: ODUCn at n=2. About 200 Gbps. + color: "#673ab7" + - name: ODUC3 + label: ODUC3 + description: ODUCn at n=3. About 300 Gbps. + color: "#673ab7" + - name: ODUC4 + label: ODUC4 + description: ODUCn at n=4. About 400 Gbps. + color: "#673ab7" + - name: ODUC6 + label: ODUC6 + description: ODUCn at n=6. About 600 Gbps. + color: "#673ab7" + - name: ODUC8 + label: ODUC8 + description: ODUCn at n=8. About 800 Gbps. + color: "#673ab7" + - name: ODUflex + label: ODUflex + description: Sized to the client rather than to a fixed rate. + color: "#00bcd4" + - name: VC-12 + label: VC-12 + description: SDH virtual container carrying a 2 Mbps E1. + color: "#ff9800" + - name: VC-4 + label: VC-4 + description: SDH virtual container carrying the 140 Mbps STM-1 payload. + color: "#ff9800" + - name: STM-N + label: STM-N + description: A whole SDH aggregate carried transparently. + color: "#ff9800" optional: false order_weight: 1100 - name: mapping_mode - kind: Text + kind: Dropdown default_value: GMP - enum: - - GMP - - BMP - - AMP + choices: + - name: GMP + label: GMP + description: Generic mapping procedure. Rate agnostic, and what a modern client uses. + color: "#2196f3" + - name: BMP + label: BMP + description: Bit-synchronous mapping procedure. Client and container clocks are locked. + color: "#4caf50" + - name: AMP + label: AMP + description: Asynchronous mapping procedure. Justification bytes absorb the clock offset. + color: "#ff9800" optional: false order_weight: 1200 - name: description From 3ec88e5e5b9bc10120808a3f64b309a61b57defd Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 15:36:59 +0200 Subject: [PATCH 21/31] feat(otn): group the sidebar into five domains and stop claiming every site is a PoP The extension had fourteen top-level sidebar entries. Nine kinds now nest under the five a user actually lands on, matching how patch_panel, modules_linecards and routing_ospf nest their own kinds. site_type no longer defaults to pop. LocationSite is shared across this library, and an optional attribute with a default still applies it on create, so loading OTN was declaring every site in an estate a PoP. A site with no stated OTN role now reports nothing. --- docs/docs/reference/otn.mdx | 12 ++++++++++-- extensions/otn/otn.yml | 10 +++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/docs/docs/reference/otn.mdx b/docs/docs/reference/otn.mdx index 4e2550d9..09828b43 100644 --- a/docs/docs/reference/otn.mdx +++ b/docs/docs/reference/otn.mdx @@ -835,7 +835,7 @@ In this context "extensions" refer to modifications or additions to the existing | ---- | ----------- | ---- | -------- | ------------- | ------- | | latitude_microdeg | Latitude in millionths of a degree. No Float kind exists. | Number | True | | | | longitude_microdeg | Longitude in millionths of a degree. No Float kind exists. | Number | True | | | -| site_type | A PoP hosting OTN equipment, or a customer site handing traffic to one. | Dropdown | True | pop | pop, customer | +| site_type | A PoP hosting OTN equipment, or a customer site handing traffic to one. | Dropdown | True | | pop, customer | #### Relationships @@ -1433,6 +1433,7 @@ nodes: label: Transceiver type icon: mdi:chip include_in_menu: true + menu_placement: OtnTransceiver human_friendly_id: - part_number__value order_by: @@ -1574,6 +1575,7 @@ nodes: label: Fiber type icon: mdi:cable-data include_in_menu: true + menu_placement: OtnFiberSpan human_friendly_id: - name__value order_by: @@ -1657,6 +1659,7 @@ nodes: label: Conduit icon: mdi:pipe include_in_menu: true + menu_placement: OtnFiberSpan human_friendly_id: - name__value order_by: @@ -1853,6 +1856,7 @@ nodes: label: Optical multiplex section icon: mdi:ray-start-end include_in_menu: true + menu_placement: OtnFiberSpan human_friendly_id: - name__value order_by: @@ -1915,6 +1919,7 @@ nodes: label: Frequency grid channel icon: mdi:sine-wave include_in_menu: true + menu_placement: OtnOpticalCarrier human_friendly_id: - channel_number__value order_by: @@ -1964,6 +1969,7 @@ nodes: label: CWDM wavelength icon: mdi:palette-swatch-variant include_in_menu: true + menu_placement: OtnOpticalCarrier human_friendly_id: - center_wavelength_nm__value order_by: @@ -2008,6 +2014,7 @@ nodes: label: Optical mode icon: mdi:waveform include_in_menu: true + menu_placement: OtnOpticalCarrier human_friendly_id: - name__value order_by: @@ -2180,6 +2187,7 @@ nodes: label: Client signal icon: mdi:import include_in_menu: true + menu_placement: OtnService human_friendly_id: - name__value order_by: @@ -2359,6 +2367,7 @@ nodes: label: Container icon: mdi:package-variant-closed include_in_menu: true + menu_placement: OtnService human_friendly_id: - name__value order_by: @@ -3328,7 +3337,6 @@ extensions: order_weight: 1700 - name: site_type kind: Dropdown - default_value: pop choices: - name: pop label: PoP diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index 286d7490..f316e331 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -643,6 +643,7 @@ nodes: label: Transceiver type icon: mdi:chip include_in_menu: true + menu_placement: OtnTransceiver human_friendly_id: - part_number__value order_by: @@ -796,6 +797,7 @@ nodes: label: Fiber type icon: mdi:cable-data include_in_menu: true + menu_placement: OtnFiberSpan human_friendly_id: - name__value order_by: @@ -881,6 +883,7 @@ nodes: label: Conduit icon: mdi:pipe include_in_menu: true + menu_placement: OtnFiberSpan human_friendly_id: - name__value order_by: @@ -1082,6 +1085,7 @@ nodes: label: Optical multiplex section icon: mdi:ray-start-end include_in_menu: true + menu_placement: OtnFiberSpan human_friendly_id: - name__value order_by: @@ -1150,6 +1154,7 @@ nodes: label: Frequency grid channel icon: mdi:sine-wave include_in_menu: true + menu_placement: OtnOpticalCarrier human_friendly_id: - channel_number__value order_by: @@ -1204,6 +1209,7 @@ nodes: label: CWDM wavelength icon: mdi:palette-swatch-variant include_in_menu: true + menu_placement: OtnOpticalCarrier human_friendly_id: - center_wavelength_nm__value order_by: @@ -1247,6 +1253,7 @@ nodes: label: Optical mode icon: mdi:waveform include_in_menu: true + menu_placement: OtnOpticalCarrier human_friendly_id: - name__value order_by: @@ -1413,6 +1420,7 @@ nodes: label: Client signal icon: mdi:import include_in_menu: true + menu_placement: OtnService human_friendly_id: - name__value order_by: @@ -1592,6 +1600,7 @@ nodes: label: Container icon: mdi:package-variant-closed include_in_menu: true + menu_placement: OtnService human_friendly_id: - name__value order_by: @@ -2688,7 +2697,6 @@ extensions: order_weight: 1700 - name: site_type kind: Dropdown - default_value: pop choices: - name: pop label: PoP From ea3df1f91cf095d16116321be26bc9cd5194da09 Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 15:42:14 +0200 Subject: [PATCH 22/31] feat(otn): nest transceivers under devices and carriers under services Three top-level entries instead of five. A pluggable is fitted into a device port and a carrier realises a service, so both nest where the relationship already exists. This library ships no menu file, so a single named OTN heading is not available; nesting is what the automatic sidebar offers, and depth three is what routing and patch panel already use. --- docs/docs/reference/otn.mdx | 2 ++ extensions/otn/otn.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docs/docs/reference/otn.mdx b/docs/docs/reference/otn.mdx index 09828b43..b441a32e 100644 --- a/docs/docs/reference/otn.mdx +++ b/docs/docs/reference/otn.mdx @@ -1518,6 +1518,7 @@ nodes: label: Transceiver icon: mdi:memory include_in_menu: true + menu_placement: OtnGenericDevice human_friendly_id: - serial__value order_by: @@ -2892,6 +2893,7 @@ nodes: label: Optical carrier icon: mdi:waves include_in_menu: true + menu_placement: OtnService human_friendly_id: - name__value order_by: diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index f316e331..5e63a900 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -732,6 +732,7 @@ nodes: label: Transceiver icon: mdi:memory include_in_menu: true + menu_placement: OtnGenericDevice human_friendly_id: - serial__value order_by: @@ -2178,6 +2179,7 @@ nodes: label: Optical carrier icon: mdi:waves include_in_menu: true + menu_placement: OtnService human_friendly_id: - name__value order_by: From 5180b832cb432941c61bfbc053cf060fd5af851f Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 16:14:40 +0200 Subject: [PATCH 23/31] refactor(otn)!: record pluggables on the library's transceiver kinds OTN shipped its own transceiver inventory beside extensions/transceiver, so a user had two places to record one module and the sidebar showed two Transceiver entries. OtnTransceiver is gone. DcimGenericTransceiver now carries otn_port, where the module is fitted, because no OTN port is a DcimEndpoint and interface cannot serve, and otn_type, which part it is. The part catalogue stays on the OTN side: OtnTransceiverType references optical modes, so moving it into extensions/transceiver would make that extension depend on this one. Its vendor Text becomes a relationship to OrganizationManufacturer, matching the Dcim field. BREAKING CHANGE: OtnTransceiver is removed. Record an optical pluggable as a DcimStandardTransceiver with otn_port and otn_type set. --- .metadata.yml | 6 +- docs/docs/reference/otn.mdx | 132 ++++++++----------------- docs/docs/reference/transceiver.mdx | 6 +- extensions/otn/otn.yml | 117 ++++++++-------------- extensions/transceiver/transceiver.yml | 4 + objects/extensions/otn/otn.yml | 44 ++++++--- 6 files changed, 124 insertions(+), 185 deletions(-) diff --git a/.metadata.yml b/.metadata.yml index 2d8ef236..dd0ad1cc 100644 --- a/.metadata.yml +++ b/.metadata.yml @@ -225,6 +225,7 @@ extensions/otn: dependencies: - base - extensions/location_site + - extensions/transceiver description: | Optical transport network schemas covering the physical plant, the wavelength catalog, optical devices and their ports, pluggable optics, carriers and end-to-end services. name: OTN @@ -247,8 +248,9 @@ extensions/otn: use_cases: - Recording the ITU-T G.694.1 dense grid and G.694.2 coarse plan as separate kinds, so a carrier pointed at the wrong plan is refused when written. - - Tracking pluggable optics as inventory, by part number and by serial, and which - port each unit is fitted in. + - Tracking pluggable optics as inventory, with the part number in this extension's + catalog, the serial on the library's transceiver kinds from extensions/transceiver, + and which optical port each unit is fitted in. - Modelling the outside plant, including fiber types, conduits, spans and the optical multiplex sections built over them. - Provisioning a wavelength end to end, as a service with an optical path and diff --git a/docs/docs/reference/otn.mdx b/docs/docs/reference/otn.mdx index b441a32e..afb63e3d 100644 --- a/docs/docs/reference/otn.mdx +++ b/docs/docs/reference/otn.mdx @@ -9,12 +9,13 @@ Optical transport network schemas covering the physical plant, the wavelength ca - **Dependencies:** - [base](dcim) - [extensions/location_site](location_site) + - [extensions/transceiver](transceiver) ## Use cases This extension covers: - Recording the ITU-T G.694.1 dense grid and G.694.2 coarse plan as separate kinds, so a carrier pointed at the wrong plan is refused when written. -- Tracking pluggable optics as inventory, by part number and by serial, and which port each unit is fitted in. +- Tracking pluggable optics as inventory, with the part number in this extension's catalog, the serial on the library's transceiver kinds from extensions/transceiver, and which optical port each unit is fitted in. - Modelling the outside plant, including fiber types, conduits, spans and the optical multiplex sections built over them. - Provisioning a wavelength end to end, as a service with an optical path and its hops through the elements the light crosses. @@ -39,7 +40,7 @@ Out of scope, and what to reach for instead: | name | peer | optional | cardinality | kind | | ---- | ---- | -------- | ----------- | ---- | -| transceiver | OtnTransceiver | True | one | Attribute | +| transceiver | DcimGenericTransceiver | True | one | Attribute | ### ClientPort @@ -53,7 +54,7 @@ Out of scope, and what to reach for instead: | name | peer | optional | cardinality | kind | | ---- | ---- | -------- | ----------- | ---- | -| transceiver | OtnTransceiver | True | one | Attribute | +| transceiver | DcimGenericTransceiver | True | one | Attribute | ### LinePort @@ -68,7 +69,7 @@ Out of scope, and what to reach for instead: | name | peer | optional | cardinality | kind | | ---- | ---- | -------- | ----------- | ---- | | carrier | OtnOpticalCarrier | True | one | Attribute | -| transceiver | OtnTransceiver | True | one | Attribute | +| transceiver | DcimGenericTransceiver | True | one | Attribute | ### RoadmAddDropPort @@ -138,7 +139,6 @@ Out of scope, and what to reach for instead: | name | description | kind | optional | default_value | choices | | ---- | ----------- | ---- | -------- | ------------- | ------- | | part_number | Vendor part number, such as QDD-400G-ZRP-S. | Text | False | | | -| vendor | | Text | True | | | | description | | Text | True | | | | form_factor | The cage this part fits. It decides which ports can hold it. | Dropdown | False | | QSFP-DD, QSFP28, OSFP, CFP2-DCO, CFP2-ACO, SFP28, SFP+ | | tunable | Whether the laser can be tuned across the grid or is fixed at one wavelength. | Boolean | False | False | | @@ -148,28 +148,7 @@ Out of scope, and what to reach for instead: | name | peer | optional | cardinality | kind | | ---- | ---- | -------- | ----------- | ---- | | supported_modes | OtnOpticalMode | True | many | Attribute | - -### Transceiver - -- **Label:** Transceiver -- **Description:** One physical pluggable optic, fitted in a port or sitting on a shelf. -- **Namespace:** Otn -- **Icon:** mdi:memory -- **Human Friendly ID:** serial__value - -#### Attributes - -| name | description | kind | optional | default_value | choices | -| ---- | ----------- | ---- | -------- | ------------- | ------- | -| serial | Vendor serial number. The only thing that identifies this unit. | Text | False | | | -| status | | Dropdown | False | spare | in_service, spare, rma, decommissioned | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| type | OtnTransceiverType | False | one | Attribute | -| port | OtnOpticalPort | True | one | Attribute | +| manufacturer | OrganizationManufacturer | True | one | Attribute | ### FiberType @@ -846,6 +825,15 @@ In this context "extensions" refer to modifications or additions to the existing | spans_b | OtnFiberSpan | True | many | Attribute | | otn_facility | OtnFacility | True | one | Attribute | +### DcimGenericTransceiver + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| otn_port | OtnOpticalPort | True | one | Attribute | +| otn_type | OtnTransceiverType | True | one | Attribute | + ## Code ```yaml @@ -1304,7 +1292,7 @@ nodes: - OtnOpticalPort relationships: - name: transceiver - peer: OtnTransceiver + peer: DcimGenericTransceiver label: Module fitted in this port kind: Attribute cardinality: one @@ -1322,7 +1310,7 @@ nodes: - OtnOpticalPort relationships: - name: transceiver - peer: OtnTransceiver + peer: DcimGenericTransceiver label: Module fitted in this port kind: Attribute cardinality: one @@ -1348,7 +1336,7 @@ nodes: identifier: otn_carrier__line_ports order_weight: 850 - name: transceiver - peer: OtnTransceiver + peer: DcimGenericTransceiver label: Module fitted in this port kind: Attribute cardinality: one @@ -1433,7 +1421,7 @@ nodes: label: Transceiver type icon: mdi:chip include_in_menu: true - menu_placement: OtnTransceiver + menu_placement: OtnGenericDevice human_friendly_id: - part_number__value order_by: @@ -1449,12 +1437,6 @@ nodes: optional: false description: Vendor part number, such as QDD-400G-ZRP-S. order_weight: 1000 - - name: vendor - kind: Text - parameters: - max_length: 64 - optional: true - order_weight: 1100 - name: description kind: Text parameters: @@ -1512,63 +1494,13 @@ nodes: optional: true identifier: otn_optical_mode__transceiver_types order_weight: 900 -- name: Transceiver - namespace: Otn - description: One physical pluggable optic, fitted in a port or sitting on a shelf. - label: Transceiver - icon: mdi:memory - include_in_menu: true - menu_placement: OtnGenericDevice - human_friendly_id: - - serial__value - order_by: - - serial__value - display_label: '{{ type__part_number__value }} {{ serial__value }}' - attributes: - - name: serial - kind: Text - unique: true - parameters: - max_length: 64 - regex: ^[A-Za-z0-9._-]+$ - optional: false - description: Vendor serial number. The only thing that identifies this unit. - order_weight: 1000 - - name: status - kind: Dropdown - default_value: spare - choices: - - name: in_service - label: In service - color: '#4caf50' - - name: spare - label: Spare - color: '#2196f3' - - name: rma - label: Returned to vendor - color: '#ff9800' - - name: decommissioned - label: Decommissioned - color: '#9e9e9e' - optional: false - order_weight: 1100 - relationships: - - name: type - peer: OtnTransceiverType - label: Part this unit is - kind: Attribute - cardinality: one - optional: false - identifier: otn_transceiver_type__units - order_weight: 900 - - name: port - peer: OtnOpticalPort - label: Port this unit is fitted in + - name: manufacturer + peer: OrganizationManufacturer kind: Attribute cardinality: one optional: true - identifier: otn_optical_port__transceiver - order_weight: 950 + identifier: otn_transceiver_type__manufacturer + order_weight: 1100 - name: FiberType namespace: Otn description: Single-mode fiber family. Attenuation, dispersion and group index at @@ -3381,5 +3313,23 @@ extensions: optional: true identifier: otn_site__facility order_weight: 930 + - kind: DcimGenericTransceiver + relationships: + - name: otn_port + peer: OtnOpticalPort + label: OTN port this unit is fitted in + kind: Attribute + cardinality: one + optional: true + identifier: otn_optical_port__transceiver + order_weight: 1600 + - name: otn_type + peer: OtnTransceiverType + label: OTN part this unit is + kind: Attribute + cardinality: one + optional: true + identifier: otn_transceiver_type__units + order_weight: 1700 ``` \ No newline at end of file diff --git a/docs/docs/reference/transceiver.mdx b/docs/docs/reference/transceiver.mdx index 2ac312bb..023d363c 100644 --- a/docs/docs/reference/transceiver.mdx +++ b/docs/docs/reference/transceiver.mdx @@ -50,7 +50,7 @@ Limitations: there is no validation between type, form factor, protocol and dist | name | description | kind | optional | default_value | choices | | ---- | ----------- | ---- | -------- | ------------- | ------- | | serial_number | | Text | True | | | -| transceiver_type | Type of transceiver, such as LR, SR, T. | Dropdown | False | | lr, sr, lrm, t, sr4, lr4, zr, er, dac, aoc | +| transceiver_type | Type of transceiver, such as LR, SR, T. | Dropdown | False | | lr, sr, lrm, t, sr4, lr4, dr4, zr, er, dac, aoc | | status | | Dropdown | False | plugged | plugged, spare, decommissioned | | form_factor | The physical form factor of the transceiver. | Dropdown | False | | sfp, sfp_plus, qsfp, qsfp_plus, qsfp28, qsfp_dd, cfp, cfp2, cfp4, xfp, sfp56, qsfp56, osfp | @@ -140,6 +140,10 @@ generics: description: Long Range 4-lane transceiver, typically used for longer distances over parallel optics. color: '#336699' + - name: dr4 + label: DR4 (500m Range 4-lane) + description: 500 m 4-lane transceiver over parallel single mode fiber. + color: '#339966' - name: zr label: ZR (Extended Reach) description: Extended Reach transceiver, suitable for distances up to 80km. diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index 5e63a900..470f420c 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -491,11 +491,12 @@ nodes: - OtnGenericPort - OtnOpticalPort relationships: - # The module fitted in this port, and the inverse of OtnTransceiver.port. Both - # ends are cardinality one, so a second module in one port is refused at write - # time. Declared only on the three concrete kinds that have a cage. + # The module fitted in this port, and the inverse of the `otn_port` added to + # DcimGenericTransceiver below. Both ends are cardinality one, so a second + # module in one port is refused at write time. Declared only on the three + # concrete kinds that have a cage. - name: transceiver - peer: OtnTransceiver + peer: DcimGenericTransceiver label: Module fitted in this port kind: Attribute cardinality: one @@ -514,7 +515,7 @@ nodes: - OtnOpticalPort relationships: - name: transceiver - peer: OtnTransceiver + peer: DcimGenericTransceiver label: Module fitted in this port kind: Attribute cardinality: one @@ -545,7 +546,7 @@ nodes: identifier: otn_carrier__line_ports order_weight: 850 - name: transceiver - peer: OtnTransceiver + peer: DcimGenericTransceiver label: Module fitted in this port kind: Attribute cardinality: one @@ -636,14 +637,14 @@ nodes: - OtnGenericPort - OtnCopperPort - # The catalog entry and the physical unit. Neither is racked, so neither inherits. + # The part catalog. The fitted unit is a DcimGenericTransceiver, extended below. - name: TransceiverType namespace: Otn description: A pluggable optic part number and what it can be made to do. label: Transceiver type icon: mdi:chip include_in_menu: true - menu_placement: OtnTransceiver + menu_placement: OtnGenericDevice human_friendly_id: - part_number__value order_by: @@ -659,12 +660,6 @@ nodes: optional: false description: Vendor part number, such as QDD-400G-ZRP-S. order_weight: 1000 - - name: vendor - kind: Text - parameters: - max_length: 64 - optional: true - order_weight: 1100 - name: description kind: Text parameters: @@ -725,70 +720,13 @@ nodes: optional: true identifier: otn_optical_mode__transceiver_types order_weight: 900 - - - name: Transceiver - namespace: Otn - description: One physical pluggable optic, fitted in a port or sitting on a shelf. - label: Transceiver - icon: mdi:memory - include_in_menu: true - menu_placement: OtnGenericDevice - human_friendly_id: - - serial__value - order_by: - - serial__value - display_label: "{{ type__part_number__value }} {{ serial__value }}" - attributes: - - name: serial - kind: Text - unique: true - parameters: - max_length: 64 - regex: "^[A-Za-z0-9._-]+$" - optional: false - description: Vendor serial number. The only thing that identifies this unit. - order_weight: 1000 - - name: status - kind: Dropdown - default_value: spare - choices: - - name: in_service - label: In service - color: "#4caf50" - - name: spare - label: Spare - color: "#2196f3" - - name: rma - label: Returned to vendor - color: "#ff9800" - - name: decommissioned - label: Decommissioned - color: "#9e9e9e" - optional: false - order_weight: 1100 - relationships: - - name: type - peer: OtnTransceiverType - label: Part this unit is - kind: Attribute - cardinality: one - optional: false - identifier: otn_transceiver_type__units - order_weight: 900 - # Optional, so a spare, an RMA and a decommissioned unit stay modellable, and - # therefore carrying no uniqueness constraint: Infrahub refuses one on an - # optional relationship. The duplicate is refused by the inverse instead, since - # every `transceiver` on this identifier is cardinality one. This peers the - # generic, and a relationship to a generic cannot be filtered by peer kind, so - # a module written into an amplifier port is accepted. Unenforced here. - - name: port - peer: OtnOpticalPort - label: Port this unit is fitted in + - name: manufacturer + peer: OrganizationManufacturer kind: Attribute cardinality: one optional: true - identifier: otn_optical_port__transceiver - order_weight: 950 + identifier: otn_transceiver_type__manufacturer + order_weight: 1100 # Plant. @@ -2740,3 +2678,32 @@ extensions: optional: true identifier: otn_site__facility order_weight: 930 + + # OTN records its pluggables on the library's transceiver kinds rather than a + # second inventory. `otn_port` is where the module is fitted, because no OTN port + # is a DcimEndpoint and `interface` cannot serve. The part catalog stays on the + # OTN side, since OtnTransceiverType references optical modes. + - kind: DcimGenericTransceiver + relationships: + # Optional, so a spare or decommissioned unit stays modellable, and therefore + # carrying no uniqueness constraint: Infrahub refuses one on an optional + # relationship. The duplicate is refused by the inverse instead, since every + # `transceiver` on this identifier is cardinality one. This peers the generic, + # and a relationship to a generic cannot be filtered by peer kind, so a module + # written into an amplifier port is accepted. Unenforced here. + - name: otn_port + peer: OtnOpticalPort + label: OTN port this unit is fitted in + kind: Attribute + cardinality: one + optional: true + identifier: otn_optical_port__transceiver + order_weight: 1600 + - name: otn_type + peer: OtnTransceiverType + label: OTN part this unit is + kind: Attribute + cardinality: one + optional: true + identifier: otn_transceiver_type__units + order_weight: 1700 diff --git a/extensions/transceiver/transceiver.yml b/extensions/transceiver/transceiver.yml index 0537b25e..d5783357 100644 --- a/extensions/transceiver/transceiver.yml +++ b/extensions/transceiver/transceiver.yml @@ -47,6 +47,10 @@ generics: label: LR4 (Long Range 4-lane) description: Long Range 4-lane transceiver, typically used for longer distances over parallel optics. color: "#336699" + - name: dr4 + label: DR4 (500m Range 4-lane) + description: 500 m 4-lane transceiver over parallel single mode fiber. + color: "#339966" - name: zr label: ZR (Extended Reach) description: Extended Reach transceiver, suitable for distances up to 80km. diff --git a/objects/extensions/otn/otn.yml b/objects/extensions/otn/otn.yml index c7f984a2..b23a7c85 100644 --- a/objects/extensions/otn/otn.yml +++ b/objects/extensions/otn/otn.yml @@ -1,7 +1,8 @@ # --------------------------------------------------------------------------- # Mock data for extensions/otn. -# Depends on: base (OrganizationProvider: Lumen), extensions/location_minimal -# (LocationSite: NYC1, SJC1). +# Depends on: base (OrganizationManufacturer: Cisco, OrganizationProvider: Lumen), +# extensions/location_minimal (LocationSite: NYC1, SJC1), extensions/transceiver +# (DcimStandardTransceiver, which records the fitted pluggable). # # One facility, one span, one wavelength and one service end to end. Enough to # walk the model from a service down to the ports it lands on, not a @@ -50,23 +51,11 @@ spec: kind: OtnTransceiverType data: - part_number: QDD-400G-DR4 - vendor: Cisco + manufacturer: Cisco description: 400G QSFP-DD grey client optic, 500 m over parallel single mode fiber. form_factor: QSFP-DD tunable: false ---- -apiVersion: infrahub.app/v1 -kind: Object -spec: - kind: OtnTransceiver - data: - # The port claims the transceiver, further down, rather than the other way - # round: `port` peers with the OtnOpticalPort generic, which has no hfid. - - serial: TRX-NYC1-0001 - status: in_service - type: QDD-400G-DR4 - --- apiVersion: infrahub.app/v1 kind: Object @@ -88,7 +77,6 @@ spec: oper_state: up connector_type: MPO-12 polish: APC - transceiver: TRX-NYC1-0001 - kind: OtnLinePort data: name: line-1/0/0 @@ -121,6 +109,30 @@ spec: tx_power_mdbm: 1000 rx_sensitivity_mdbm: -25000 +--- +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: DcimStandardTransceiver + data: + # The fitted module. This loads after the transponder above, because + # DcimGenericTransceiver declares no hfid, so nothing can reference it by name: + # the transceiver has to claim its port rather than the port claiming it. + - serial_number: TRX-NYC1-0001 + transceiver_type: dr4 + form_factor: qsfp_dd + status: plugged + manufacturer: Cisco + otn_type: QDD-400G-DR4 + # `otn_port` peers the OtnOpticalPort generic, which has no hfid, so the port + # names its concrete kind and upserts by its uniqueness fields. + otn_port: + kind: OtnClientPort + data: + name: client-1/0/0 + role: client + device: nyc1-txp01 + --- apiVersion: infrahub.app/v1 kind: Object From eb6ae96ba24d75e787a38df6ee03d6fd7106bcba Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 19:12:01 +0200 Subject: [PATCH 24/31] feat(otn): add plant lifecycle state, protection paths and amplifier ratings The outside plant had no operational state, so a span or a section could not be marked planned or under maintenance while a device could. A service could not carry a protect path beside its working one, because OtnOpticalPath had no role and its uniqueness constraint allowed one path per sequence. Both were modelled by the extension this branch replaces. OtnService regains maintenance and failed, OtnOpticalCarrier gains reserved so a wavelength can be held before it is lit, and OtnAmplifier gains its nameplate type, stage and output ceiling. The dense grid's scope is now stated: one 50 GHz C band plan, with L band, C plus L and flexgrid out of scope. --- .metadata.yml | 3 + docs/docs/reference/otn.mdx | 183 +++++++++++++++++++++++++++++++++++- extensions/otn/otn.yml | 163 +++++++++++++++++++++++++++++++- 3 files changed, 345 insertions(+), 4 deletions(-) diff --git a/.metadata.yml b/.metadata.yml index dd0ad1cc..58182d61 100644 --- a/.metadata.yml +++ b/.metadata.yml @@ -245,6 +245,9 @@ extensions/otn: - Optical path budgets. OtnOpticalPath records which elements the light crosses and in what order, not the loss, OSNR margin or latency of that route. Compute those in a planning tool such as GNPy. + - Frequency plans other than one fixed dense grid. OtnFrequencyGrid models a single + ITU-T G.694.1 fixed 50 GHz plan across the C band, from 191.35 to 196.10 THz. + L band, C plus L systems, flexgrid and other channel spacings are out of scope. use_cases: - Recording the ITU-T G.694.1 dense grid and G.694.2 coarse plan as separate kinds, so a carrier pointed at the wrong plan is refused when written. diff --git a/docs/docs/reference/otn.mdx b/docs/docs/reference/otn.mdx index afb63e3d..176812a4 100644 --- a/docs/docs/reference/otn.mdx +++ b/docs/docs/reference/otn.mdx @@ -25,6 +25,7 @@ Out of scope, and what to reach for instead: - Rack elevations, device types and platforms for optical gear. Model the physical asset as a DcimPhysicalDevice and link it with OtnGenericDevice.dcim_device. - Optical performance monitoring. The schema carries ratings and settings, such as a port's transmit power or a mode's required OSNR, but nothing that is measured, such as live power, OSNR, pre-FEC BER or an error counter. Those change continuously and belong in the network management system or a time series store. - Optical path budgets. OtnOpticalPath records which elements the light crosses and in what order, not the loss, OSNR margin or latency of that route. Compute those in a planning tool such as GNPy. +- Frequency plans other than one fixed dense grid. OtnFrequencyGrid models a single ITU-T G.694.1 fixed 50 GHz plan across the C band, from 191.35 to 196.10 THz. L band, C plus L systems, flexgrid and other channel spacings are out of scope. ## Nodes @@ -191,6 +192,7 @@ Out of scope, and what to reach for instead: | name | | Text | False | | | | owner | Who owns the trench. Often not the network operator. | Text | True | | | | description | | Text | True | | | +| status | Lifecycle state of the duct itself, independent of the spans pulled through it. | Dropdown | False | active | planned, active, standby, maintenance, failed, decommissioned | #### Relationships @@ -214,6 +216,7 @@ Out of scope, and what to reach for instead: | name | | Text | False | | | | description | | Text | True | | | | oms_sequence | Position of this span within its optical multiplex section, counting from the A end. | Number | True | | | +| status | Lifecycle state of the span. A planned span is designed but not yet spliced through. | Dropdown | False | active | planned, active, standby, maintenance, failed, decommissioned | | length_m | Route length in metres, not straight-line distance. 500 km is the practical ceiling. | Number | False | | | | length_display | Route length in km. Read-only, derived from length_m. | Text | True | | | | splice_count | Fusion splices along the span. Roughly one per drum of cable. | Number | False | 0 | | @@ -248,6 +251,7 @@ Out of scope, and what to reach for instead: | ---- | ----------- | ---- | -------- | ------------- | ------- | | name | | Text | False | | | | description | | Text | True | | | +| status | Lifecycle state of the section as a whole, not of the spans it groups. | Dropdown | False | active | planned, active, standby, maintenance, failed, decommissioned | #### Relationships @@ -430,11 +434,15 @@ Out of scope, and what to reach for instead: | name | description | kind | optional | default_value | choices | | ---- | ----------- | ---- | -------- | ------------- | ------- | +| amplifier_type | Nameplate amplifier technology. A Raman-only pump is its own kind, OtnRamanPump. | Dropdown | True | | edfa, soa, hybrid | +| stage | Nameplate stage this amplifier is built for, not a position derived from the plant. | Dropdown | True | | booster, inline, preamp | | noise_figure_mdb | Noise figure in millidecibels. 4.0 dB is 4000. Sets the ASE this stage adds. | Number | False | 4000 | | | noise_figure_display | Noise figure in dB. Read-only, derived from noise_figure_mdb. | Text | True | | | | gain_mdb | Gain in millidecibels. 22.0 dB is 22000. Must cover the loss ahead of the input. | Number | False | 22000 | | | gain_display | Gain in dB. Read-only, derived from gain_mdb. | Text | True | | | | oms_sequence | Position in this amplifier's own chain, counting along the direction it amplifies. | Number | False | | | +| max_total_output_power_mdbm | Rated total output power in milli-dBm, 0 to +30 dBm. A nameplate ceiling, not a reading. | Number | True | | | +| max_total_output_power_display | Rated total output power in dBm. Read-only, derived from max_total_output_power_mdbm. | Text | True | | | #### Relationships @@ -555,7 +563,7 @@ Out of scope, and what to reach for instead: | ---- | ----------- | ---- | -------- | ------------- | ------- | | name | | Text | False | | | | description | | Text | True | | | -| status | | Dropdown | False | active | planned, active, decommissioned | +| status | | Dropdown | False | active | planned, reserved, active, decommissioned | #### Relationships @@ -586,7 +594,7 @@ Out of scope, and what to reach for instead: | description | | Text | True | | | | rate_gbps | Requested client capacity in Gbps. Already whole in its natural unit. | Number | False | | | | sla | | Dropdown | False | silver | gold, silver, bronze, best_effort | -| status | | Dropdown | False | planned | planned, provisioning, active, rejected, decommissioned | +| status | | Dropdown | False | planned | planned, provisioning, active, maintenance, failed, rejected, decommissioned | | service_profile | | Dropdown | False | ip-transit | ai-training-dci, ai-inference, hpc-research, ip-transit, legacy-sdh | | max_latency_ns | One-way latency budget in nanoseconds. Null on the profiles that have none. | Number | True | | | | max_latency_display | Latency budget in microseconds. Read-only, derived from max_latency_ns. | Text | True | | | @@ -631,7 +639,7 @@ Out of scope, and what to reach for instead: - **Namespace:** Otn - **Icon:** mdi:map-marker-path - **Uniqueness Constraints:** - - service, segment_sequence__value + - service, segment_sequence__value, path_role__value - **Human Friendly ID:** name__value #### Attributes @@ -641,6 +649,7 @@ Out of scope, and what to reach for instead: | name | | Text | False | | | | description | | Text | True | | | | segment_sequence | Which segment of its circuit this path is. 1 for a circuit that spans one wavelength. | Number | False | 1 | | +| path_role | Which role this path plays for its service, working, protect or express. | Dropdown | False | working | working, protect, express | #### Relationships @@ -1620,6 +1629,32 @@ nodes: max_length: 256 optional: true order_weight: 1200 + - name: status + kind: Dropdown + default_value: active + choices: + - name: planned + label: Planned + color: '#2196f3' + - name: active + label: Active + color: '#4caf50' + - name: standby + label: Standby + color: '#00bcd4' + - name: maintenance + label: Maintenance + color: '#ffc107' + - name: failed + label: Failed + color: '#ff5722' + - name: decommissioned + label: Decommissioned + color: '#9e9e9e' + optional: false + description: Lifecycle state of the duct itself, independent of the spans pulled + through it. + order_weight: 1300 relationships: - name: spans peer: OtnFiberSpan @@ -1666,6 +1701,32 @@ nodes: description: Position of this span within its optical multiplex section, counting from the A end. order_weight: 1200 + - name: status + kind: Dropdown + default_value: active + choices: + - name: planned + label: Planned + color: '#2196f3' + - name: active + label: Active + color: '#4caf50' + - name: standby + label: Standby + color: '#00bcd4' + - name: maintenance + label: Maintenance + color: '#ffc107' + - name: failed + label: Failed + color: '#ff5722' + - name: decommissioned + label: Decommissioned + color: '#9e9e9e' + optional: false + description: Lifecycle state of the span. A planned span is designed but not yet + spliced through. + order_weight: 1300 - name: length_m kind: Number parameters: @@ -1810,6 +1871,31 @@ nodes: max_length: 256 optional: true order_weight: 1100 + - name: status + kind: Dropdown + default_value: active + choices: + - name: planned + label: Planned + color: '#2196f3' + - name: active + label: Active + color: '#4caf50' + - name: standby + label: Standby + color: '#00bcd4' + - name: maintenance + label: Maintenance + color: '#ffc107' + - name: failed + label: Failed + color: '#ff5722' + - name: decommissioned + label: Decommissioned + color: '#9e9e9e' + optional: false + description: Lifecycle state of the section as a whole, not of the spans it groups. + order_weight: 1200 relationships: - name: roadm_a peer: OtnRoadm @@ -2535,6 +2621,47 @@ nodes: - OtnGenericDevice - OtnOpticalElement attributes: + - name: amplifier_type + kind: Dropdown + choices: + - name: edfa + label: EDFA + description: Erbium-doped fibre amplifier. The usual choice on a C band line + system. + color: '#2196f3' + - name: soa + label: SOA + description: Semiconductor optical amplifier. Compact, noisier, found in access + gear. + color: '#ff9800' + - name: hybrid + label: Hybrid + description: An erbium stage combined with distributed Raman gain in one box. + color: '#673ab7' + optional: true + description: Nameplate amplifier technology. A Raman-only pump is its own kind, + OtnRamanPump. + order_weight: 1300 + - name: stage + kind: Dropdown + choices: + - name: booster + label: Booster + description: First stage after the transmitter, lifting power into the span. + color: '#ff9800' + - name: inline + label: Inline + description: A mid-route stage that recovers span loss and passes the light + on. + color: '#4caf50' + - name: preamp + label: Pre-amplifier + description: Last stage before the receiver, raising power to its sensitivity. + color: '#ffc107' + optional: true + description: Nameplate stage this amplifier is built for, not a position derived + from the plant. + order_weight: 1400 - name: noise_figure_mdb kind: Number default_value: 4000 @@ -2584,6 +2711,25 @@ nodes: description: Position in this amplifier's own chain, counting along the direction it amplifies. order_weight: 1560 + - name: max_total_output_power_mdbm + kind: Number + parameters: + min_value: 0 + max_value: 30000 + optional: true + description: Rated total output power in milli-dBm, 0 to +30 dBm. A nameplate + ceiling, not a reading. + order_weight: 1570 + - name: max_total_output_power_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if max_total_output_power_mdbm__value is not none %}{{ + max_total_output_power_mdbm__value / 1000 }} dBm{% endif %}' + optional: true + description: Rated total output power in dBm. Read-only, derived from max_total_output_power_mdbm. + order_weight: 1580 relationships: - name: oms_a2b peer: OtnOpticalMultiplexSection @@ -2853,6 +2999,9 @@ nodes: - name: planned label: Planned color: '#2196f3' + - name: reserved + label: Reserved + color: '#ff9800' - name: active label: Active color: '#4caf50' @@ -2981,6 +3130,12 @@ nodes: - name: active label: Active color: '#4caf50' + - name: maintenance + label: Maintenance + color: '#ffc107' + - name: failed + label: Failed + color: '#ff5722' - name: rejected label: Rejected color: '#f44336' @@ -3130,6 +3285,7 @@ nodes: uniqueness_constraints: - - service - segment_sequence__value + - path_role__value attributes: - name: name kind: Text @@ -3155,6 +3311,27 @@ nodes: description: Which segment of its circuit this path is. 1 for a circuit that spans one wavelength. order_weight: 1200 + - name: path_role + kind: Dropdown + default_value: working + choices: + - name: working + label: Working + description: The route the service takes while nothing on it has failed. + color: '#4caf50' + - name: protect + label: Protect + description: The standby route the service switches to when its working one + fails. + color: '#2196f3' + - name: express + label: Express + description: A route passing a site optically, with nothing added or dropped + there. + color: '#9c27b0' + optional: false + description: Which role this path plays for its service, working, protect or express. + order_weight: 1300 relationships: - name: service peer: OtnService diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index 470f420c..8fa5b89f 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -850,6 +850,31 @@ nodes: max_length: 256 optional: true order_weight: 1200 + - name: status + kind: Dropdown + default_value: active + choices: + - name: planned + label: Planned + color: "#2196f3" + - name: active + label: Active + color: "#4caf50" + - name: standby + label: Standby + color: "#00bcd4" + - name: maintenance + label: Maintenance + color: "#ffc107" + - name: failed + label: Failed + color: "#ff5722" + - name: decommissioned + label: Decommissioned + color: "#9e9e9e" + optional: false + description: Lifecycle state of the duct itself, independent of the spans pulled through it. + order_weight: 1300 relationships: - name: spans peer: OtnFiberSpan @@ -899,6 +924,31 @@ nodes: optional: true description: Position of this span within its optical multiplex section, counting from the A end. order_weight: 1200 + - name: status + kind: Dropdown + default_value: active + choices: + - name: planned + label: Planned + color: "#2196f3" + - name: active + label: Active + color: "#4caf50" + - name: standby + label: Standby + color: "#00bcd4" + - name: maintenance + label: Maintenance + color: "#ffc107" + - name: failed + label: Failed + color: "#ff5722" + - name: decommissioned + label: Decommissioned + color: "#9e9e9e" + optional: false + description: Lifecycle state of the span. A planned span is designed but not yet spliced through. + order_weight: 1300 - name: length_m kind: Number parameters: @@ -1047,6 +1097,31 @@ nodes: max_length: 256 optional: true order_weight: 1100 + - name: status + kind: Dropdown + default_value: active + choices: + - name: planned + label: Planned + color: "#2196f3" + - name: active + label: Active + color: "#4caf50" + - name: standby + label: Standby + color: "#00bcd4" + - name: maintenance + label: Maintenance + color: "#ffc107" + - name: failed + label: Failed + color: "#ff5722" + - name: decommissioned + label: Decommissioned + color: "#9e9e9e" + optional: false + description: Lifecycle state of the section as a whole, not of the spans it groups. + order_weight: 1200 relationships: - name: roadm_a peer: OtnRoadm @@ -1791,6 +1866,43 @@ nodes: # oms_sequence is stored because Infrahub relationships carry no order, so a # section hands back a set and a budget cannot be walked over one. attributes: + - name: amplifier_type + kind: Dropdown + choices: + - name: edfa + label: EDFA + description: Erbium-doped fibre amplifier. The usual choice on a C band line system. + color: "#2196f3" + - name: soa + label: SOA + description: Semiconductor optical amplifier. Compact, noisier, found in access gear. + color: "#ff9800" + - name: hybrid + label: Hybrid + description: An erbium stage combined with distributed Raman gain in one box. + color: "#673ab7" + optional: true + description: Nameplate amplifier technology. A Raman-only pump is its own kind, OtnRamanPump. + order_weight: 1300 + # OtnGenericPort.role carries booster and preamp for ports. This is the device's own role in the chain. + - name: stage + kind: Dropdown + choices: + - name: booster + label: Booster + description: First stage after the transmitter, lifting power into the span. + color: "#ff9800" + - name: inline + label: Inline + description: A mid-route stage that recovers span loss and passes the light on. + color: "#4caf50" + - name: preamp + label: Pre-amplifier + description: Last stage before the receiver, raising power to its sensitivity. + color: "#ffc107" + optional: true + description: Nameplate stage this amplifier is built for, not a position derived from the plant. + order_weight: 1400 # 3000 is the quantum-limited floor for an EDFA and 10000 is a bad one. - name: noise_figure_mdb kind: Number @@ -1845,6 +1957,25 @@ nodes: optional: false description: Position in this amplifier's own chain, counting along the direction it amplifies. order_weight: 1560 + - name: max_total_output_power_mdbm + kind: Number + parameters: + min_value: 0 + max_value: 30000 + optional: true + description: Rated total output power in milli-dBm, 0 to +30 dBm. A nameplate ceiling, not a reading. + order_weight: 1570 + - name: max_total_output_power_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if max_total_output_power_mdbm__value is not none %}{{ + max_total_output_power_mdbm__value / 1000 }} dBm{% endif %} + optional: true + description: Rated total output power in dBm. Read-only, derived from max_total_output_power_mdbm. + order_weight: 1580 # The two inverses of the section's two amplifier relationships, each on the # matching identifier. Exactly one is set, and which one answers the direction. relationships: @@ -2146,6 +2277,9 @@ nodes: - name: planned label: Planned color: "#2196f3" + - name: reserved + label: Reserved + color: "#ff9800" - name: active label: Active color: "#4caf50" @@ -2291,6 +2425,12 @@ nodes: - name: active label: Active color: "#4caf50" + - name: maintenance + label: Maintenance + color: "#ffc107" + - name: failed + label: Failed + color: "#ff5722" - name: rejected label: Rejected color: "#f44336" @@ -2471,7 +2611,9 @@ nodes: # constraint sits on, which is why it cannot be declared on `OtnService`. uniqueness_constraints: # It refuses a repeat of segment 2 and is blind to the sequence 1, 2, 4. - - ["service", "segment_sequence__value"] + # A service's protect path is a second OtnOpticalPath at the same segment_sequence + # with path_role protect, so the working and protect routes hang off one service. + - ["service", "segment_sequence__value", "path_role__value"] attributes: - name: name kind: Text @@ -2499,6 +2641,25 @@ nodes: optional: false description: Which segment of its circuit this path is. 1 for a circuit that spans one wavelength. order_weight: 1200 + - name: path_role + kind: Dropdown + default_value: working + choices: + - name: working + label: Working + description: The route the service takes while nothing on it has failed. + color: "#4caf50" + - name: protect + label: Protect + description: The standby route the service switches to when its working one fails. + color: "#2196f3" + - name: express + label: Express + description: A route passing a site optically, with nothing added or dropped there. + color: "#9c27b0" + optional: false + description: Which role this path plays for its service, working, protect or express. + order_weight: 1300 relationships: # Mandatory. A path with no service has no meaning here: it is one wavelength's # route chosen to answer one customer's request, and its figures come from that. From 5ab0cb1d8c1883496c5ab9c80d710991a69d6ccd Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 20:06:41 +0200 Subject: [PATCH 25/31] chore: leave the mlag comment's stale dwdm mention alone objects/extensions/dwdm never existed, so that mention is a pre-existing error rather than something this change made stale. It is fixed separately, outside this branch. --- objects/extensions/mlag/mlag.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/objects/extensions/mlag/mlag.yml b/objects/extensions/mlag/mlag.yml index e98fd2c1..99857226 100644 --- a/objects/extensions/mlag/mlag.yml +++ b/objects/extensions/mlag/mlag.yml @@ -25,7 +25,7 @@ # So a fixture that owns a relationship owns its endpoints too. This file # creates, and is the only file anywhere under objects/ that ever saves, # nyc1-rtr02 and nyc1-rtr03. It deliberately does not build the domain on -# nyc1-rtr01: objects/extensions/rack, device_psu_module, routing_ospf +# nyc1-rtr01: objects/extensions/rack, device_psu_module, dwdm, routing_ospf # and routing_pim all re-upsert that device after this file has run. # objects/extensions/routing_bgp and peering_ixp only reference nyc1-rtr02 by # human_friendly_id, which does not save it and so is safe. From af6079017ef210573c641d2b134143ed0a0932fd Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 7 Sep 2026 20:15:41 +0200 Subject: [PATCH 26/31] feat(otn): add capacity, CDC flags, lifecycle dates and degree direction Five gaps the removed extensions covered and this one did not. Capacity gives utilisation a denominator, so occupied channels can be read against a nameplate. The CDC flags are planning inputs for which wavelength can be added where. The three dates are the extension's first, and they record intended state rather than a reading: a commissioning date is entered once, unlike a measurement, which is why the monitoring exclusion still holds. Degree direction and the express role restore how operators label a ROADM's degrees, and external_circuit_id gives a partner's own reference somewhere to live beside our name for the circuit. --- docs/docs/reference/otn.mdx | 134 +++++++++++++++++++++++++++++++++++- extensions/otn/otn.yml | 96 ++++++++++++++++++++++++++ 2 files changed, 229 insertions(+), 1 deletion(-) diff --git a/docs/docs/reference/otn.mdx b/docs/docs/reference/otn.mdx index 176812a4..ac8eaf6e 100644 --- a/docs/docs/reference/otn.mdx +++ b/docs/docs/reference/otn.mdx @@ -88,6 +88,13 @@ Out of scope, and what to reach for instead: - **Icon:** mdi:compass-outline - **Inherit From:** OtnGenericPort, OtnOpticalPort +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| direction | How operators label this degree. Until now the port name was the only place to say it. | Dropdown | True | | north, south, east, west, express, local | +| wavelength_capacity | Nameplate wavelengths this degree is rated to pass, such as 40, 80 or 96. Not a count in use. | Number | True | | | + ### AmplifierPort - **Label:** Amplifier port @@ -217,6 +224,7 @@ Out of scope, and what to reach for instead: | description | | Text | True | | | | oms_sequence | Position of this span within its optical multiplex section, counting from the A end. | Number | True | | | | status | Lifecycle state of the span. A planned span is designed but not yet spliced through. | Dropdown | False | active | planned, active, standby, maintenance, failed, decommissioned | +| commissioned_date | When the span was accepted into service. A recorded fact, not a reading. | DateTime | True | | | | length_m | Route length in metres, not straight-line distance. 500 km is the practical ceiling. | Number | False | | | | length_display | Route length in km. Read-only, derived from length_m. | Text | True | | | | splice_count | Fusion splices along the span. Roughly one per drum of cable. | Number | False | 0 | | @@ -415,6 +423,15 @@ Out of scope, and what to reach for instead: - **Icon:** mdi:call-split - **Inherit From:** OtnGenericDevice, OtnOpticalElement +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| channel_capacity | Nameplate channels this ROADM is rated for, such as 40, 80 or 96. Not a count in use. | Number | True | | | +| colorless | Any add/drop port can take any wavelength, so assignment is not fixed per port. | Boolean | True | | | +| directionless | An added wavelength can leave by any degree, so assignment does not pick a degree. | Boolean | True | | | +| contentionless | The same wavelength can be added twice toward different degrees without blocking. | Boolean | True | | | + #### Relationships | name | peer | optional | cardinality | kind | @@ -459,6 +476,12 @@ Out of scope, and what to reach for instead: - **Icon:** mdi:call-merge - **Inherit From:** OtnGenericDevice, OtnOpticalElement +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| channel_capacity | Nameplate channels this multiplexer is rated for, such as 8, 16, 40 or 96. Not a count in use. | Number | True | | | + #### Relationships | name | peer | optional | cardinality | kind | @@ -564,6 +587,7 @@ Out of scope, and what to reach for instead: | name | | Text | False | | | | description | | Text | True | | | | status | | Dropdown | False | active | planned, reserved, active, decommissioned | +| provisioned_date | When the wavelength was turned up. A recorded fact, not a reading. | DateTime | True | | | #### Relationships @@ -592,9 +616,11 @@ Out of scope, and what to reach for instead: | ---- | ----------- | ---- | -------- | ------------- | ------- | | name | | Text | False | | | | description | | Text | True | | | +| external_circuit_id | The customer's or partner's own reference for this circuit, where `name` is ours. | Text | True | | | | rate_gbps | Requested client capacity in Gbps. Already whole in its natural unit. | Number | False | | | | sla | | Dropdown | False | silver | gold, silver, bronze, best_effort | | status | | Dropdown | False | planned | planned, provisioning, active, maintenance, failed, rejected, decommissioned | +| provisioned_date | When the circuit was handed to the customer. A recorded fact, not a reading. | DateTime | True | | | | service_profile | | Dropdown | False | ip-transit | ai-training-dci, ai-inference, hpc-research, ip-transit, legacy-sdh | | max_latency_ns | One-way latency budget in nanoseconds. Null on the profiles that have none. | Number | True | | | | max_latency_display | Latency budget in microseconds. Read-only, derived from max_latency_ns. | Text | True | | | @@ -719,7 +745,7 @@ Out of scope, and what to reach for instead: | name | description | kind | optional | default_value | choices | | ---- | ----------- | ---- | -------- | ------------- | ------- | | name | Port identifier as the vendor labels it, such as 1/1/1. | Text | False | | | -| role | | Dropdown | False | spare | client, line, add_drop, degree, booster, preamp, tributary, spare, monitor | +| role | | Dropdown | False | spare | client, line, add_drop, degree, express, booster, preamp, tributary, spare, monitor | | enabled | | Boolean | False | True | | | admin_state | RFC 2863 ifAdminStatus. What the operator asked for. | Dropdown | False | up | up, down, testing | | oper_state | RFC 2863 ifOperStatus subset. What the port actually does. | Dropdown | False | down | up, down, testing, dormant, unknown | @@ -888,6 +914,11 @@ generics: - name: degree label: Degree color: '#673ab7' + - name: express + label: Express + description: A degree-to-degree pass-through. Wavelengths cross the node without + adding or dropping. + color: '#00bcd4' - name: booster label: Booster color: '#ff9800' @@ -1370,6 +1401,45 @@ nodes: inherit_from: - OtnGenericPort - OtnOpticalPort + attributes: + - name: direction + kind: Dropdown + choices: + - name: north + label: North + color: '#2196f3' + - name: south + label: South + color: '#4caf50' + - name: east + label: East + color: '#ff9800' + - name: west + label: West + color: '#9c27b0' + - name: express + label: Express + description: Faces another degree rather than the outside world. Nothing adds + or drops here. + color: '#00bcd4' + - name: local + label: Local + description: Faces add/drop equipment in the same building instead of a line + route. + color: '#9e9e9e' + optional: true + description: How operators label this degree. Until now the port name was the + only place to say it. + order_weight: 1500 + - name: wavelength_capacity + kind: Number + parameters: + min_value: 1 + max_value: 96 + optional: true + description: Nameplate wavelengths this degree is rated to pass, such as 40, 80 + or 96. Not a count in use. + order_weight: 1520 - name: AmplifierPort namespace: Otn description: Amplifier input or output. @@ -1727,6 +1797,11 @@ nodes: description: Lifecycle state of the span. A planned span is designed but not yet spliced through. order_weight: 1300 + - name: commissioned_date + kind: DateTime + optional: true + description: When the span was accepted into service. A recorded fact, not a reading. + order_weight: 1400 - name: length_m kind: Number parameters: @@ -2593,6 +2668,34 @@ nodes: inherit_from: - OtnGenericDevice - OtnOpticalElement + attributes: + - name: channel_capacity + kind: Number + parameters: + min_value: 1 + max_value: 96 + optional: true + description: Nameplate channels this ROADM is rated for, such as 40, 80 or 96. + Not a count in use. + order_weight: 1300 + - name: colorless + kind: Boolean + optional: true + description: Any add/drop port can take any wavelength, so assignment is not fixed + per port. + order_weight: 1320 + - name: directionless + kind: Boolean + optional: true + description: An added wavelength can leave by any degree, so assignment does not + pick a degree. + order_weight: 1330 + - name: contentionless + kind: Boolean + optional: true + description: The same wavelength can be added twice toward different degrees without + blocking. + order_weight: 1340 relationships: - name: sections_a peer: OtnOpticalMultiplexSection @@ -2756,6 +2859,16 @@ nodes: inherit_from: - OtnGenericDevice - OtnOpticalElement + attributes: + - name: channel_capacity + kind: Number + parameters: + min_value: 1 + max_value: 96 + optional: true + description: Nameplate channels this multiplexer is rated for, such as 8, 16, + 40 or 96. Not a count in use. + order_weight: 1300 relationships: - name: cwdm_channels peer: OtnCwdmChannel @@ -3010,6 +3123,11 @@ nodes: color: '#9e9e9e' optional: false order_weight: 1200 + - name: provisioned_date + kind: DateTime + optional: true + description: When the wavelength was turned up. A recorded fact, not a reading. + order_weight: 1300 relationships: - name: channel peer: OtnFrequencyGrid @@ -3091,6 +3209,14 @@ nodes: max_length: 256 optional: true order_weight: 1100 + - name: external_circuit_id + kind: Text + parameters: + max_length: 64 + optional: true + description: The customer's or partner's own reference for this circuit, where + `name` is ours. + order_weight: 1200 - name: rate_gbps kind: Number parameters: @@ -3144,6 +3270,12 @@ nodes: color: '#9e9e9e' optional: false order_weight: 1500 + - name: provisioned_date + kind: DateTime + optional: true + description: When the circuit was handed to the customer. A recorded fact, not + a reading. + order_weight: 1520 - name: service_profile kind: Dropdown default_value: ip-transit diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index 8fa5b89f..9c3d06c4 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -56,6 +56,10 @@ generics: - name: degree label: Degree color: "#673ab7" + - name: express + label: Express + description: A degree-to-degree pass-through. Wavelengths cross the node without adding or dropping. + color: "#00bcd4" - name: booster label: Booster color: "#ff9800" @@ -573,6 +577,41 @@ nodes: inherit_from: - OtnGenericPort - OtnOpticalPort + attributes: + - name: direction + kind: Dropdown + choices: + - name: north + label: North + color: "#2196f3" + - name: south + label: South + color: "#4caf50" + - name: east + label: East + color: "#ff9800" + - name: west + label: West + color: "#9c27b0" + - name: express + label: Express + description: Faces another degree rather than the outside world. Nothing adds or drops here. + color: "#00bcd4" + - name: local + label: Local + description: Faces add/drop equipment in the same building instead of a line route. + color: "#9e9e9e" + optional: true + description: How operators label this degree. Until now the port name was the only place to say it. + order_weight: 1500 + - name: wavelength_capacity + kind: Number + parameters: + min_value: 1 + max_value: 96 + optional: true + description: Nameplate wavelengths this degree is rated to pass, such as 40, 80 or 96. Not a count in use. + order_weight: 1520 - name: AmplifierPort namespace: Otn @@ -949,6 +988,13 @@ nodes: optional: false description: Lifecycle state of the span. A planned span is designed but not yet spliced through. order_weight: 1300 + # The extension's first DateTime. A commissioning or provisioning date is intended state, + # written once by a person; a measurement moves on its own and stays in the management system. + - name: commissioned_date + kind: DateTime + optional: true + description: When the span was accepted into service. A recorded fact, not a reading. + order_weight: 1400 - name: length_m kind: Number parameters: @@ -1833,6 +1879,30 @@ nodes: inherit_from: - OtnGenericDevice - OtnOpticalElement + attributes: + - name: channel_capacity + kind: Number + parameters: + min_value: 1 + max_value: 96 + optional: true + description: Nameplate channels this ROADM is rated for, such as 40, 80 or 96. Not a count in use. + order_weight: 1300 + - name: colorless + kind: Boolean + optional: true + description: Any add/drop port can take any wavelength, so assignment is not fixed per port. + order_weight: 1320 + - name: directionless + kind: Boolean + optional: true + description: An added wavelength can leave by any degree, so assignment does not pick a degree. + order_weight: 1330 + - name: contentionless + kind: Boolean + optional: true + description: The same wavelength can be added twice toward different degrees without blocking. + order_weight: 1340 # The inverses of OtnOpticalMultiplexSection.roadm_a and .roadm_b, each on the # identifier its forward side declares. relationships: @@ -2005,6 +2075,15 @@ nodes: inherit_from: - OtnGenericDevice - OtnOpticalElement + attributes: + - name: channel_capacity + kind: Number + parameters: + min_value: 1 + max_value: 96 + optional: true + description: Nameplate channels this multiplexer is rated for, such as 8, 16, 40 or 96. Not a count in use. + order_weight: 1300 relationships: # The wavelengths a coarse multiplexer lights are a property of the device # rather than of the plan. @@ -2288,6 +2367,11 @@ nodes: color: "#9e9e9e" optional: false order_weight: 1200 + - name: provisioned_date + kind: DateTime + optional: true + description: When the wavelength was turned up. A recorded fact, not a reading. + order_weight: 1300 relationships: - name: channel peer: OtnFrequencyGrid @@ -2382,6 +2466,13 @@ nodes: max_length: 256 optional: true order_weight: 1100 + - name: external_circuit_id + kind: Text + parameters: + max_length: 64 + optional: true + description: The customer's or partner's own reference for this circuit, where `name` is ours. + order_weight: 1200 # Customer intent, not a modulation format. The mode chosen to meet it lands on # the carrier. - name: rate_gbps @@ -2439,6 +2530,11 @@ nodes: color: "#9e9e9e" optional: false order_weight: 1500 + - name: provisioned_date + kind: DateTime + optional: true + description: When the circuit was handed to the customer. A recorded fact, not a reading. + order_weight: 1520 # The first three profiles carry a latency budget, the last two leave it null. - name: service_profile kind: Dropdown From 161d05de829db0ef21c1a153e793cce5c61410c9 Mon Sep 17 00:00:00 2001 From: Iddo Date: Tue, 8 Sep 2026 09:36:29 +0200 Subject: [PATCH 27/31] refactor(otn)!: cable OTN ports and derive what the model already knows Every OTN port kind now inherits DcimEndpoint, so extensions/cable terminates on optical gear and an intra-site jumper is a DcimCable rather than a port attribute. OtnGenericPort.connected_to is gone: in the G.872 layering a section owns its adjacency and a port only terminates it, so the cable, OtnFiberSpan.terminating_ports and OtnOpticalPath already answer that question at their own layers. A cardinality-one symmetric edge could model neither a 1:N splitter nor a unidirectional element, and it flattened the panel out of a run that physically passes through one. Removes two attributes that restated what the graph already held. element_class duplicated the node kind and could contradict it, since an OtnMuxDemux was free to declare itself a roadm. OtnFrequencyGrid stored channel_number beside center_frequency_mhz with the formula living only in a description, and the demo data had already drifted, pairing channel 21 with 192.1 THz, which is channel 16 on the 50 GHz plan this extension models. channel_number and OtnCwdmChannel.band are computed now, so neither can disagree with the quantity it derives from. Computed attributes are Text or URL only, so the grid's identity moves to center_frequency_mhz, which stays a Number and keeps its range filter. An optical multiplex section terminates on the new OtnOmsEndpoint generic instead of OtnRoadm, so a fixed point-to-point system built from passive multiplexers is modellable, and with it the amplifier chain and the carrier sections that hang off a section. OtnRouter becomes an OtnOpticalElement, so an IPoDWDM path can hop through the routers on its own ends and a router carries the vendor and model every other device kind has. Adds plan, mux_role and technology to OtnMuxDemux, which gives a fixed OADM a home; technology to OtnRoadm; description to OtnGenericPort; and a derived centre wavelength to the dense grid. BREAKING CHANGE: element_class, connected_to and the stored channel_number are removed, and OtnOpticalMultiplexSection.roadm_a and roadm_b become endpoint_a and endpoint_b. The Dropdown to Text changes cannot be applied over an instance carrying the previous schema, which fails with "Can only specify 'choices' for kind=Dropdown"; load onto a fresh instance instead. --- .metadata.yml | 22 ++- docs/docs/reference/otn.mdx | 320 ++++++++++++++++++++------------- extensions/otn/otn.yml | 292 +++++++++++++++++++----------- objects/extensions/otn/otn.yml | 48 +++-- 4 files changed, 425 insertions(+), 257 deletions(-) diff --git a/.metadata.yml b/.metadata.yml index 58182d61..f8654d23 100644 --- a/.metadata.yml +++ b/.metadata.yml @@ -230,14 +230,15 @@ extensions/otn: Optical transport network schemas covering the physical plant, the wavelength catalog, optical devices and their ports, pluggable optics, carriers and end-to-end services. name: OTN not_covered: - - Cabling between optical devices. No OTN port is a DcimEndpoint, so extensions/cable - cannot terminate on optical gear. Fiber is modelled as OtnFiberSpan and OtnConduit - instead. - Validation of what a schema cannot express, such as which port kind may hold - a pluggable, or whether a mux client port binds exactly one channel. This extension - ships no checks; the schema comments mark each such rule. - - Rack elevations, device types and platforms for optical gear. Model the physical - asset as a DcimPhysicalDevice and link it with OtnGenericDevice.dcim_device. + a pluggable, or whether a mux client port binds a channel on the same plan its + multiplexer declares. This extension ships no checks; the schema comments mark + each such rule. + - Device types and platforms for optical gear. Model the physical asset as a DcimPhysicalDevice + and link it with OtnGenericDevice.dcim_device. That edge is also the route to + a rack elevation, since the physical record carries position, rack_face and + a location that may be a LocationRack. Nothing keeps OtnGenericDevice.site in + agreement with the site above that rack. - Optical performance monitoring. The schema carries ratings and settings, such as a port's transmit power or a mode's required OSNR, but nothing that is measured, such as live power, OSNR, pre-FEC BER or an error counter. Those change continuously @@ -255,7 +256,12 @@ extensions/otn: catalog, the serial on the library's transceiver kinds from extensions/transceiver, and which optical port each unit is fitted in. - Modelling the outside plant, including fiber types, conduits, spans and the - optical multiplex sections built over them. + optical multiplex sections built over them. A section terminates on any OMS + endpoint, so a fixed point-to-point system built from passive multiplexers is + modellable without a ROADM. + - Cabling optical gear with extensions/cable. Every OTN port kind inherits DcimEndpoint, + so a DcimCable terminates on one for intra-site jumpers, through a DcimPatchPanel + where a panel sits in the run. - Provisioning a wavelength end to end, as a service with an optical path and its hops through the elements the light crosses. extensions/patch_panel: diff --git a/docs/docs/reference/otn.mdx b/docs/docs/reference/otn.mdx index ac8eaf6e..58ea5b5a 100644 --- a/docs/docs/reference/otn.mdx +++ b/docs/docs/reference/otn.mdx @@ -16,13 +16,13 @@ Optical transport network schemas covering the physical plant, the wavelength ca This extension covers: - Recording the ITU-T G.694.1 dense grid and G.694.2 coarse plan as separate kinds, so a carrier pointed at the wrong plan is refused when written. - Tracking pluggable optics as inventory, with the part number in this extension's catalog, the serial on the library's transceiver kinds from extensions/transceiver, and which optical port each unit is fitted in. -- Modelling the outside plant, including fiber types, conduits, spans and the optical multiplex sections built over them. +- Modelling the outside plant, including fiber types, conduits, spans and the optical multiplex sections built over them. A section terminates on any OMS endpoint, so a fixed point-to-point system built from passive multiplexers is modellable without a ROADM. +- Cabling optical gear with extensions/cable. Every OTN port kind inherits DcimEndpoint, so a DcimCable terminates on one for intra-site jumpers, through a DcimPatchPanel where a panel sits in the run. - Provisioning a wavelength end to end, as a service with an optical path and its hops through the elements the light crosses. Out of scope, and what to reach for instead: -- Cabling between optical devices. No OTN port is a DcimEndpoint, so extensions/cable cannot terminate on optical gear. Fiber is modelled as OtnFiberSpan and OtnConduit instead. -- Validation of what a schema cannot express, such as which port kind may hold a pluggable, or whether a mux client port binds exactly one channel. This extension ships no checks; the schema comments mark each such rule. -- Rack elevations, device types and platforms for optical gear. Model the physical asset as a DcimPhysicalDevice and link it with OtnGenericDevice.dcim_device. +- Validation of what a schema cannot express, such as which port kind may hold a pluggable, or whether a mux client port binds a channel on the same plan its multiplexer declares. This extension ships no checks; the schema comments mark each such rule. +- Device types and platforms for optical gear. Model the physical asset as a DcimPhysicalDevice and link it with OtnGenericDevice.dcim_device. That edge is also the route to a rack elevation, since the physical record carries position, rack_face and a location that may be a LocationRack. Nothing keeps OtnGenericDevice.site in agreement with the site above that rack. - Optical performance monitoring. The schema carries ratings and settings, such as a port's transmit power or a mode's required OSNR, but nothing that is measured, such as live power, OSNR, pre-FEC BER or an error counter. Those change continuously and belong in the network management system or a time series store. - Optical path budgets. OtnOpticalPath records which elements the light crosses and in what order, not the loss, OSNR margin or latency of that route. Compute those in a planning tool such as GNPy. - Frequency plans other than one fixed dense grid. OtnFrequencyGrid models a single ITU-T G.694.1 fixed 50 GHz plan across the C band, from 191.35 to 196.10 THz. L band, C plus L systems, flexgrid and other channel spacings are out of scope. @@ -35,7 +35,7 @@ Out of scope, and what to reach for instead: - **Description:** Grey optics on an IP router. Not on the C-band grid. - **Namespace:** Otn - **Icon:** mdi:ethernet -- **Inherit From:** OtnGenericPort, OtnOpticalPort +- **Inherit From:** DcimEndpoint, OtnGenericPort, OtnOpticalPort #### Relationships @@ -49,7 +49,7 @@ Out of scope, and what to reach for instead: - **Description:** Transponder client side. Where grey light arrives. - **Namespace:** Otn - **Icon:** mdi:lan-connect -- **Inherit From:** OtnGenericPort, OtnOpticalPort +- **Inherit From:** DcimEndpoint, OtnGenericPort, OtnOpticalPort #### Relationships @@ -63,7 +63,7 @@ Out of scope, and what to reach for instead: - **Description:** Transponder DWDM line side. Where coloured light leaves. - **Namespace:** Otn - **Icon:** mdi:transit-connection -- **Inherit From:** OtnGenericPort, OtnOpticalPort +- **Inherit From:** DcimEndpoint, OtnGenericPort, OtnOpticalPort #### Relationships @@ -78,7 +78,7 @@ Out of scope, and what to reach for instead: - **Description:** Local add and drop on a ROADM. - **Namespace:** Otn - **Icon:** mdi:call-split -- **Inherit From:** OtnGenericPort, OtnOpticalPort +- **Inherit From:** DcimEndpoint, OtnGenericPort, OtnOpticalPort ### RoadmDegreePort @@ -86,7 +86,7 @@ Out of scope, and what to reach for instead: - **Description:** Line-facing degree on a ROADM. One per direction. - **Namespace:** Otn - **Icon:** mdi:compass-outline -- **Inherit From:** OtnGenericPort, OtnOpticalPort +- **Inherit From:** DcimEndpoint, OtnGenericPort, OtnOpticalPort #### Attributes @@ -101,7 +101,7 @@ Out of scope, and what to reach for instead: - **Description:** Amplifier input or output. - **Namespace:** Otn - **Icon:** mdi:amplifier -- **Inherit From:** OtnGenericPort, OtnOpticalPort +- **Inherit From:** DcimEndpoint, OtnGenericPort, OtnOpticalPort ### MuxClientPort @@ -109,7 +109,7 @@ Out of scope, and what to reach for instead: - **Description:** One channel of a multiplexer. The side facing the transponder or router that lights it. - **Namespace:** Otn - **Icon:** mdi:import -- **Inherit From:** OtnGenericPort, OtnOpticalPort +- **Inherit From:** DcimEndpoint, OtnGenericPort, OtnOpticalPort #### Relationships @@ -124,7 +124,7 @@ Out of scope, and what to reach for instead: - **Description:** The common side of a multiplexer, where the whole band leaves. - **Namespace:** Otn - **Icon:** mdi:export -- **Inherit From:** OtnGenericPort, OtnOpticalPort +- **Inherit From:** DcimEndpoint, OtnGenericPort, OtnOpticalPort ### TributaryPort @@ -132,7 +132,7 @@ Out of scope, and what to reach for instead: - **Description:** E1 or T1 G.703 electrical tributary. The only copper port here. - **Namespace:** Otn - **Icon:** mdi:cable-data -- **Inherit From:** OtnGenericPort, OtnCopperPort +- **Inherit From:** DcimEndpoint, OtnGenericPort, OtnCopperPort ### TransceiverType @@ -248,7 +248,7 @@ Out of scope, and what to reach for instead: ### OpticalMultiplexSection - **Label:** Optical multiplex section -- **Description:** ROADM to ROADM. Groups the ordered spans and inline amplifiers between two degrees. +- **Description:** Groups the ordered spans and inline amplifiers between two OMS endpoints. - **Namespace:** Otn - **Icon:** mdi:ray-start-end - **Human Friendly ID:** name__value @@ -265,8 +265,8 @@ Out of scope, and what to reach for instead: | name | peer | optional | cardinality | kind | | ---- | ---- | -------- | ----------- | ---- | -| roadm_a | OtnRoadm | False | one | Attribute | -| roadm_b | OtnRoadm | False | one | Attribute | +| endpoint_a | OtnOmsEndpoint | False | one | Attribute | +| endpoint_b | OtnOmsEndpoint | False | one | Attribute | | spans | OtnFiberSpan | True | many | Attribute | | amplifiers_a2b | OtnAmplifier | True | many | Attribute | | amplifiers_b2a | OtnAmplifier | True | many | Attribute | @@ -277,15 +277,16 @@ Out of scope, and what to reach for instead: - **Description:** One ITU-T G.694.1 channel. 50 GHz fixed grid, 191.35 to 196.10 THz. - **Namespace:** Otn - **Icon:** mdi:sine-wave -- **Human Friendly ID:** channel_number__value +- **Human Friendly ID:** center_frequency_mhz__value #### Attributes | name | description | kind | optional | default_value | choices | | ---- | ----------- | ---- | -------- | ------------- | ------- | -| channel_number | ITU channel number, 1 to 96. Channel 1 is 191.35 THz. | Number | False | | | +| channel_number | ITU channel number, 1 to 96. Channel 1 is 191.35 THz. Derived from the centre frequency. | Text | True | | | | center_frequency_mhz | Centre frequency in MHz. Channel n is 191350000 + (n - 1) x 50000. | Number | False | | | | center_frequency_display | Centre frequency in THz. Byte-identical to the optical-port rendering. | Text | True | | | +| center_wavelength_display | Centre wavelength in nm, derived from the centre frequency. | Text | True | | | #### Relationships @@ -306,7 +307,7 @@ Out of scope, and what to reach for instead: | name | description | kind | optional | default_value | choices | | ---- | ----------- | ---- | -------- | ------------- | ------- | | center_wavelength_nm | Nominal central wavelength in nm. Wavelength n is 1271 + (n - 1) x 20. | Number | False | | | -| band | ITU band. Only the two C-band wavelengths sit in the erbium window. | Dropdown | False | | o, e, s, c, l | +| band | ITU band, derived from the wavelength. Only the two C-band wavelengths sit in the erbium window. | Text | True | | | ### OpticalMode @@ -402,10 +403,10 @@ Out of scope, and what to reach for instead: ### Router - **Label:** Router -- **Description:** IP router. Light terminates here, so it contributes no insertion loss. +- **Description:** IP router. Light terminates here, so its insertion loss stays at the zero default. - **Namespace:** Otn - **Icon:** mdi:router -- **Inherit From:** OtnGenericDevice +- **Inherit From:** OtnGenericDevice, OtnOpticalElement ### Transponder @@ -421,7 +422,7 @@ Out of scope, and what to reach for instead: - **Description:** Reconfigurable optical add/drop multiplexer. - **Namespace:** Otn - **Icon:** mdi:call-split -- **Inherit From:** OtnGenericDevice, OtnOpticalElement +- **Inherit From:** OtnGenericDevice, OtnOmsEndpoint, OtnOpticalElement #### Attributes @@ -431,13 +432,7 @@ Out of scope, and what to reach for instead: | colorless | Any add/drop port can take any wavelength, so assignment is not fixed per port. | Boolean | True | | | | directionless | An added wavelength can leave by any degree, so assignment does not pick a degree. | Boolean | True | | | | contentionless | The same wavelength can be added twice toward different degrees without blocking. | Boolean | True | | | - -#### Relationships - -| name | peer | optional | cardinality | kind | -| ---- | ---- | -------- | ----------- | ---- | -| sections_a | OtnOpticalMultiplexSection | True | many | Attribute | -| sections_b | OtnOpticalMultiplexSection | True | many | Attribute | +| technology | How the switch is built. Nameplate data, often unknown. | Dropdown | True | | wss_lcos, wss_mems, broadcast_select | ### Amplifier @@ -474,13 +469,16 @@ Out of scope, and what to reach for instead: - **Description:** Passive multiplexer. A dense AWG on the core, a coarse thin-film filter on a tail. - **Namespace:** Otn - **Icon:** mdi:call-merge -- **Inherit From:** OtnGenericDevice, OtnOpticalElement +- **Inherit From:** OtnGenericDevice, OtnOmsEndpoint, OtnOpticalElement #### Attributes | name | description | kind | optional | default_value | choices | | ---- | ----------- | ---- | -------- | ------------- | ------- | | channel_capacity | Nameplate channels this multiplexer is rated for, such as 8, 16, 40 or 96. Not a count in use. | Number | True | | | +| plan | Which wavelength plan this multiplexer multiplexes. | Dropdown | False | dwdm | dwdm, cwdm | +| mux_role | What this passive device does with the channels it carries. | Dropdown | False | mux_demux | mux, demux, mux_demux, oadm | +| technology | How the device separates wavelengths. Nameplate data, often unknown. | Dropdown | True | | thin_film_filter, awg, interleaver | #### Relationships @@ -745,6 +743,7 @@ Out of scope, and what to reach for instead: | name | description | kind | optional | default_value | choices | | ---- | ----------- | ---- | -------- | ------------- | ------- | | name | Port identifier as the vendor labels it, such as 1/1/1. | Text | False | | | +| description | | Text | True | | | | role | | Dropdown | False | spare | client, line, add_drop, degree, express, booster, preamp, tributary, spare, monitor | | enabled | | Boolean | False | True | | | admin_state | RFC 2863 ifAdminStatus. What the operator asked for. | Dropdown | False | up | up, down, testing | @@ -755,7 +754,6 @@ Out of scope, and what to reach for instead: | name | peer | optional | cardinality | kind | | ---- | ---- | -------- | ----------- | ---- | | device | OtnGenericDevice | False | one | Parent | -| connected_to | OtnGenericPort | True | one | Attribute | ### OpticalElement @@ -772,7 +770,20 @@ Out of scope, and what to reach for instead: | insertion_loss_display | Insertion loss in dB. Read-only, derived from insertion_loss_mdb. | Text | True | | | | vendor | | Text | True | | | | model | | Text | True | | | -| element_class | Which class of optical element this is. Set it to match the kind. | Dropdown | False | | transponder, roadm, amplifier, mux_demux, patch_panel, fiber_span, splitter, attenuator, raman_pump, odu_switch | + +### OmsEndpoint + +- **Label:** OMS endpoint +- **Description:** A device that can terminate an optical multiplex section. +- **Namespace:** Otn +- **Icon:** mdi:ray-start-end + +#### Relationships + +| name | peer | optional | cardinality | kind | +| ---- | ---- | -------- | ----------- | ---- | +| sections_a | OtnOpticalMultiplexSection | True | many | Attribute | +| sections_b | OtnOpticalMultiplexSection | True | many | Attribute | ### GenericDevice @@ -898,6 +909,12 @@ generics: optional: false description: Port identifier as the vendor labels it, such as 1/1/1. order_weight: 1000 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1100 - name: role kind: Dropdown default_value: spare @@ -988,13 +1005,6 @@ generics: optional: false identifier: otn_device__ports order_weight: 800 - - name: connected_to - peer: OtnGenericPort - kind: Attribute - cardinality: one - optional: true - identifier: otn_port__connected_to - order_weight: 900 - name: OpticalElement namespace: Otn description: Anything light passes through and loses power in. @@ -1033,42 +1043,29 @@ generics: max_length: 64 optional: true order_weight: 1700 - - name: element_class - kind: Dropdown - choices: - - name: transponder - label: Transponder - color: '#2196f3' - - name: roadm - label: ROADM - color: '#9c27b0' - - name: amplifier - label: Amplifier - color: '#ff9800' - - name: mux_demux - label: Mux/demux - color: '#00bcd4' - - name: patch_panel - label: Patch panel - color: '#9e9e9e' - - name: fiber_span - label: Fiber span - color: '#4caf50' - - name: splitter - label: Splitter - color: '#8bc34a' - - name: attenuator - label: Attenuator - color: '#795548' - - name: raman_pump - label: Raman pump - color: '#e91e63' - - name: odu_switch - label: ODU switch - color: '#673ab7' - optional: false - description: Which class of optical element this is. Set it to match the kind. - order_weight: 1800 +- name: OmsEndpoint + namespace: Otn + description: A device that can terminate an optical multiplex section. + label: OMS endpoint + icon: mdi:ray-start-end + include_in_menu: false + relationships: + - name: sections_a + peer: OtnOpticalMultiplexSection + label: Sections (A end) + kind: Attribute + cardinality: many + optional: true + identifier: otn_oms__endpoint_a + order_weight: 960 + - name: sections_b + peer: OtnOpticalMultiplexSection + label: Sections (B end) + kind: Attribute + cardinality: many + optional: true + identifier: otn_oms__endpoint_b + order_weight: 970 - name: GenericDevice namespace: Otn description: Anything racked at a site. @@ -1328,6 +1325,7 @@ nodes: icon: mdi:ethernet include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnOpticalPort relationships: @@ -1346,6 +1344,7 @@ nodes: icon: mdi:lan-connect include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnOpticalPort relationships: @@ -1364,6 +1363,7 @@ nodes: icon: mdi:transit-connection include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnOpticalPort relationships: @@ -1390,6 +1390,7 @@ nodes: icon: mdi:call-split include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnOpticalPort - name: RoadmDegreePort @@ -1399,6 +1400,7 @@ nodes: icon: mdi:compass-outline include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnOpticalPort attributes: @@ -1447,6 +1449,7 @@ nodes: icon: mdi:amplifier include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnOpticalPort - name: MuxClientPort @@ -1457,6 +1460,7 @@ nodes: icon: mdi:import include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnOpticalPort relationships: @@ -1483,6 +1487,7 @@ nodes: icon: mdi:export include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnOpticalPort - name: TributaryPort @@ -1492,6 +1497,7 @@ nodes: icon: mdi:cable-data include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnCopperPort - name: TransceiverType @@ -1920,8 +1926,7 @@ nodes: order_weight: 950 - name: OpticalMultiplexSection namespace: Otn - description: ROADM to ROADM. Groups the ordered spans and inline amplifiers between - two degrees. + description: Groups the ordered spans and inline amplifiers between two OMS endpoints. label: Optical multiplex section icon: mdi:ray-start-end include_in_menu: true @@ -1972,19 +1977,19 @@ nodes: description: Lifecycle state of the section as a whole, not of the spans it groups. order_weight: 1200 relationships: - - name: roadm_a - peer: OtnRoadm + - name: endpoint_a + peer: OtnOmsEndpoint kind: Attribute cardinality: one optional: false - identifier: otn_oms__roadm_a + identifier: otn_oms__endpoint_a order_weight: 800 - - name: roadm_b - peer: OtnRoadm + - name: endpoint_b + peer: OtnOmsEndpoint kind: Attribute cardinality: one optional: false - identifier: otn_oms__roadm_b + identifier: otn_oms__endpoint_b order_weight: 810 - name: spans peer: OtnFiberSpan @@ -2015,19 +2020,21 @@ nodes: include_in_menu: true menu_placement: OtnOpticalCarrier human_friendly_id: - - channel_number__value + - center_frequency_mhz__value order_by: - - channel_number__value + - center_frequency_mhz__value display_label: Ch{{ channel_number__value }} attributes: - name: channel_number - kind: Number - unique: true - parameters: - min_value: 1 - max_value: 96 - optional: false - description: ITU channel number, 1 to 96. Channel 1 is 191.35 THz. + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if center_frequency_mhz__value is not none %}{{ ((center_frequency_mhz__value + - 191350000) // 50000) + 1 }}{% endif %}' + optional: true + description: ITU channel number, 1 to 96. Channel 1 is 191.35 THz. Derived from + the centre frequency. order_weight: 1000 - name: center_frequency_mhz kind: Number @@ -2048,6 +2055,16 @@ nodes: optional: true description: Centre frequency in THz. Byte-identical to the optical-port rendering. order_weight: 1510 + - name: center_wavelength_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if center_frequency_mhz__value is not none %}{{ (299792458000 + / center_frequency_mhz__value) | round(2) }} nm{% endif %}' + optional: true + description: Centre wavelength in nm, derived from the centre frequency. + order_weight: 1520 relationships: - name: carriers peer: OtnOpticalCarrier @@ -2081,25 +2098,17 @@ nodes: x 20. order_weight: 1000 - name: band - kind: Dropdown - choices: - - name: o - label: O band - color: '#607d8b' - - name: e - label: E band - color: '#795548' - - name: s - label: S band - color: '#009688' - - name: c - label: C band - color: '#2196f3' - - name: l - label: L band - color: '#9c27b0' - optional: false - description: ITU band. Only the two C-band wavelengths sit in the erbium window. + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: '{% if center_wavelength_nm__value is not none %}{% if center_wavelength_nm__value + < 1360 %}O{% elif center_wavelength_nm__value < 1460 %}E{% elif center_wavelength_nm__value + < 1530 %}S{% elif center_wavelength_nm__value < 1565 %}C{% else %}L{% endif + %}{% endif %}' + optional: true + description: ITU band, derived from the wavelength. Only the two C-band wavelengths + sit in the erbium window. order_weight: 1100 - name: OpticalMode namespace: Otn @@ -2641,13 +2650,15 @@ nodes: order_weight: 900 - name: Router namespace: Otn - description: IP router. Light terminates here, so it contributes no insertion loss. + description: IP router. Light terminates here, so its insertion loss stays at the + zero default. label: Router icon: mdi:router include_in_menu: true menu_placement: OtnGenericDevice inherit_from: - OtnGenericDevice + - OtnOpticalElement - name: Transponder namespace: Otn description: Client to DWDM line adaptation. @@ -2667,6 +2678,7 @@ nodes: menu_placement: OtnGenericDevice inherit_from: - OtnGenericDevice + - OtnOmsEndpoint - OtnOpticalElement attributes: - name: channel_capacity @@ -2696,23 +2708,21 @@ nodes: description: The same wavelength can be added twice toward different degrees without blocking. order_weight: 1340 - relationships: - - name: sections_a - peer: OtnOpticalMultiplexSection - label: Sections (A end) - kind: Attribute - cardinality: many - optional: true - identifier: otn_oms__roadm_a - order_weight: 960 - - name: sections_b - peer: OtnOpticalMultiplexSection - label: Sections (B end) - kind: Attribute - cardinality: many + - name: technology + kind: Dropdown + choices: + - name: wss_lcos + label: WSS (LCoS) + color: '#673ab7' + - name: wss_mems + label: WSS (MEMS) + color: '#3f51b5' + - name: broadcast_select + label: Broadcast and select + color: '#607d8b' optional: true - identifier: otn_oms__roadm_b - order_weight: 970 + description: How the switch is built. Nameplate data, often unknown. + order_weight: 1350 - name: Amplifier namespace: Otn description: Inline, booster or pre-amplifier. @@ -2858,6 +2868,7 @@ nodes: menu_placement: OtnGenericDevice inherit_from: - OtnGenericDevice + - OtnOmsEndpoint - OtnOpticalElement attributes: - name: channel_capacity @@ -2869,6 +2880,59 @@ nodes: description: Nameplate channels this multiplexer is rated for, such as 8, 16, 40 or 96. Not a count in use. order_weight: 1300 + - name: plan + kind: Dropdown + default_value: dwdm + choices: + - name: dwdm + label: DWDM + description: ITU-T G.694.1 dense grid. + color: '#2196f3' + - name: cwdm + label: CWDM + description: ITU-T G.694.2 coarse plan. + color: '#ff9800' + optional: false + description: Which wavelength plan this multiplexer multiplexes. + order_weight: 1310 + - name: mux_role + kind: Dropdown + default_value: mux_demux + choices: + - name: mux + label: Multiplexer + description: Combines channels onto one common port. + color: '#4caf50' + - name: demux + label: Demultiplexer + description: Splits one common port into channels. + color: '#00bcd4' + - name: mux_demux + label: Mux/demux + description: Both directions in one device. + color: '#2196f3' + - name: oadm + label: Fixed OADM + description: Drops and adds a fixed subset, passing the rest through. + color: '#9c27b0' + optional: false + description: What this passive device does with the channels it carries. + order_weight: 1320 + - name: technology + kind: Dropdown + choices: + - name: thin_film_filter + label: Thin-film filter + color: '#795548' + - name: awg + label: AWG + color: '#607d8b' + - name: interleaver + label: Interleaver + color: '#009688' + optional: true + description: How the device separates wavelengths. Nameplate data, often unknown. + order_weight: 1330 relationships: - name: cwdm_channels peer: OtnCwdmChannel diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index 9c3d06c4..b6d21044 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -40,6 +40,12 @@ generics: optional: false description: Port identifier as the vendor labels it, such as 1/1/1. order_weight: 1000 + - name: description + kind: Text + parameters: + max_length: 256 + optional: true + order_weight: 1100 - name: role kind: Dropdown default_value: spare @@ -129,15 +135,12 @@ generics: optional: false identifier: otn_device__ports order_weight: 800 - # Symmetric and self-referencing, so both ends are the same edge. The explicit - # identifier stops Infrahub splitting it into two one-way links. - - name: connected_to - peer: OtnGenericPort - kind: Attribute - cardinality: one - optional: true - identifier: otn_port__connected_to - order_weight: 900 + # No port-to-port adjacency attribute lives here. In the G.872 layering a + # section owns its adjacency and a port only terminates it, so intra-site + # jumpers are a DcimCable across the `connector` every port kind inherits from + # DcimEndpoint, inter-site plant is OtnFiberSpan.terminating_ports, and where + # the light goes is OtnOpticalPath. A `connected_to` edge here would be a + # fourth answer to a question those three already answer. - name: OpticalElement namespace: Otn @@ -179,42 +182,35 @@ generics: max_length: 64 optional: true order_weight: 1700 - - name: element_class - kind: Dropdown - choices: - - name: transponder - label: Transponder - color: "#2196f3" - - name: roadm - label: ROADM - color: "#9c27b0" - - name: amplifier - label: Amplifier - color: "#ff9800" - - name: mux_demux - label: Mux/demux - color: "#00bcd4" - - name: patch_panel - label: Patch panel - color: "#9e9e9e" - - name: fiber_span - label: Fiber span - color: "#4caf50" - - name: splitter - label: Splitter - color: "#8bc34a" - - name: attenuator - label: Attenuator - color: "#795548" - - name: raman_pump - label: Raman pump - color: "#e91e63" - - name: odu_switch - label: ODU switch - color: "#673ab7" - optional: false - description: Which class of optical element this is. Set it to match the kind. - order_weight: 1800 + + # What an optical multiplex section is allowed to terminate on. A ROADM switches + # channels and a passive mux filters them, but either one bounds a section, so a + # fixed point-to-point DWDM system with no ROADM in it is still modellable. The + # generic stays a marker plus the two inverses; a device's own fields live on its + # kind. + - name: OmsEndpoint + namespace: Otn + description: A device that can terminate an optical multiplex section. + label: OMS endpoint + icon: mdi:ray-start-end + include_in_menu: false + relationships: + - name: sections_a + peer: OtnOpticalMultiplexSection + label: Sections (A end) + kind: Attribute + cardinality: many + optional: true + identifier: otn_oms__endpoint_a + order_weight: 960 + - name: sections_b + peer: OtnOpticalMultiplexSection + label: Sections (B end) + kind: Attribute + cardinality: many + optional: true + identifier: otn_oms__endpoint_b + order_weight: 970 - name: GenericDevice namespace: Otn @@ -287,9 +283,9 @@ generics: order_weight: 800 # An OTN device names the optical role; DcimPhysicalDevice names the physical # asset. This edge joins the two records for one box rather than duplicating - # it, and it does not merge the two graphs: no OTN port is a DcimEndpoint, so - # extensions/cable still cannot terminate on optical gear. Optional, because a - # deployment with no Dcim inventory is equally valid. + # it, and it is also the route to a rack elevation: the physical record carries + # `position`, `rack_face` and a `location` that may be a LocationRack. Optional, + # because a deployment with no Dcim inventory is equally valid. - name: dcim_device peer: DcimPhysicalDevice label: Physical device record @@ -492,6 +488,7 @@ nodes: icon: mdi:ethernet include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnOpticalPort relationships: @@ -515,6 +512,7 @@ nodes: icon: mdi:lan-connect include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnOpticalPort relationships: @@ -534,6 +532,7 @@ nodes: icon: mdi:transit-connection include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnOpticalPort relationships: @@ -565,6 +564,7 @@ nodes: icon: mdi:call-split include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnOpticalPort @@ -575,6 +575,7 @@ nodes: icon: mdi:compass-outline include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnOpticalPort attributes: @@ -620,6 +621,7 @@ nodes: icon: mdi:amplifier include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnOpticalPort @@ -633,6 +635,7 @@ nodes: icon: mdi:import include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnOpticalPort # A channel port should bind exactly one of these two edges. Infrahub has no @@ -663,6 +666,7 @@ nodes: icon: mdi:export include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnOpticalPort @@ -673,6 +677,7 @@ nodes: icon: mdi:cable-data include_in_menu: false inherit_from: + - DcimEndpoint - OtnGenericPort - OtnCopperPort @@ -1116,7 +1121,7 @@ nodes: - name: OpticalMultiplexSection namespace: Otn - description: ROADM to ROADM. Groups the ordered spans and inline amplifiers between two degrees. + description: Groups the ordered spans and inline amplifiers between two OMS endpoints. label: Optical multiplex section icon: mdi:ray-start-end include_in_menu: true @@ -1169,19 +1174,19 @@ nodes: description: Lifecycle state of the section as a whole, not of the spans it groups. order_weight: 1200 relationships: - - name: roadm_a - peer: OtnRoadm + - name: endpoint_a + peer: OtnOmsEndpoint kind: Attribute cardinality: one optional: false - identifier: otn_oms__roadm_a + identifier: otn_oms__endpoint_a order_weight: 800 - - name: roadm_b - peer: OtnRoadm + - name: endpoint_b + peer: OtnOmsEndpoint kind: Attribute cardinality: one optional: false - identifier: otn_oms__roadm_b + identifier: otn_oms__endpoint_b order_weight: 810 - name: spans peer: OtnFiberSpan @@ -1216,19 +1221,26 @@ nodes: include_in_menu: true menu_placement: OtnOpticalCarrier human_friendly_id: - - channel_number__value + - center_frequency_mhz__value order_by: - - channel_number__value + - center_frequency_mhz__value display_label: "Ch{{ channel_number__value }}" attributes: + # Derived, not stored. In G.694.1 the frequency is normative and the channel + # number is a naming convention over it, so storing both invites the two to + # disagree. Computed attributes are Text or URL only, which is why the + # frequency stays the stored Number and keeps its range filter. - name: channel_number - kind: Number - unique: true - parameters: - min_value: 1 - max_value: 96 - optional: false - description: ITU channel number, 1 to 96. Channel 1 is 191.35 THz. + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if center_frequency_mhz__value is not none -%} + {{ ((center_frequency_mhz__value - 191350000) // 50000) + 1 }} + {%- endif %} + optional: true + description: ITU channel number, 1 to 96. Channel 1 is 191.35 THz. Derived from the centre frequency. order_weight: 1000 # Stored rather than computed, so "which channel is 193.5 THz" is a range filter. - name: center_frequency_mhz @@ -1250,6 +1262,20 @@ nodes: optional: true description: Centre frequency in THz. Byte-identical to the optical-port rendering. order_weight: 1510 + # Operators read the dense grid in nm as often as in THz. Derived from the + # frequency with c = 299792458 m/s, so the two can never disagree. + - name: center_wavelength_display + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if center_frequency_mhz__value is not none -%} + {{ (299792458000 / center_frequency_mhz__value) | round(2) }} nm + {%- endif %} + optional: true + description: Centre wavelength in nm, derived from the centre frequency. + order_weight: 1520 # The inverse of OtnOpticalCarrier.channel, on the same identifier. relationships: - name: carriers @@ -1285,26 +1311,24 @@ nodes: optional: false description: Nominal central wavelength in nm. Wavelength n is 1271 + (n - 1) x 20. order_weight: 1000 + # Derived, not stored. The ITU bands are wavelength ranges, so the band a + # coarse wavelength sits in follows from the wavelength itself. - name: band - kind: Dropdown - choices: - - name: o - label: O band - color: "#607d8b" - - name: e - label: E band - color: "#795548" - - name: s - label: S band - color: "#009688" - - name: c - label: C band - color: "#2196f3" - - name: l - label: L band - color: "#9c27b0" - optional: false - description: ITU band. Only the two C-band wavelengths sit in the erbium window. + kind: Text + read_only: true + computed_attribute: + kind: Jinja2 + jinja2_template: >- + {% if center_wavelength_nm__value is not none -%} + {% if center_wavelength_nm__value < 1360 -%}O + {%- elif center_wavelength_nm__value < 1460 -%}E + {%- elif center_wavelength_nm__value < 1530 -%}S + {%- elif center_wavelength_nm__value < 1565 -%}C + {%- else -%}L + {%- endif %} + {%- endif %} + optional: true + description: ITU band, derived from the wavelength. Only the two C-band wavelengths sit in the erbium window. order_weight: 1100 - name: OpticalMode @@ -1850,13 +1874,16 @@ nodes: # Devices. - name: Router namespace: Otn - description: IP router. Light terminates here, so it contributes no insertion loss. + description: IP router. Light terminates here, so its insertion loss stays at the zero default. label: Router icon: mdi:router include_in_menu: true menu_placement: OtnGenericDevice + # An optical element, so an IPoDWDM path can hop through the routers on its own + # ends. It also picks up the vendor and model fields every other device kind has. inherit_from: - OtnGenericDevice + - OtnOpticalElement - name: Transponder namespace: Otn @@ -1878,6 +1905,7 @@ nodes: menu_placement: OtnGenericDevice inherit_from: - OtnGenericDevice + - OtnOmsEndpoint - OtnOpticalElement attributes: - name: channel_capacity @@ -1903,25 +1931,21 @@ nodes: optional: true description: The same wavelength can be added twice toward different degrees without blocking. order_weight: 1340 - # The inverses of OtnOpticalMultiplexSection.roadm_a and .roadm_b, each on the - # identifier its forward side declares. - relationships: - - name: sections_a - peer: OtnOpticalMultiplexSection - label: Sections (A end) - kind: Attribute - cardinality: many - optional: true - identifier: otn_oms__roadm_a - order_weight: 960 - - name: sections_b - peer: OtnOpticalMultiplexSection - label: Sections (B end) - kind: Attribute - cardinality: many + - name: technology + kind: Dropdown + choices: + - name: wss_lcos + label: WSS (LCoS) + color: "#673ab7" + - name: wss_mems + label: WSS (MEMS) + color: "#3f51b5" + - name: broadcast_select + label: Broadcast and select + color: "#607d8b" optional: true - identifier: otn_oms__roadm_b - order_weight: 970 + description: How the switch is built. Nameplate data, often unknown. + order_weight: 1350 - name: Amplifier namespace: Otn @@ -2074,6 +2098,7 @@ nodes: menu_placement: OtnGenericDevice inherit_from: - OtnGenericDevice + - OtnOmsEndpoint - OtnOpticalElement attributes: - name: channel_capacity @@ -2084,6 +2109,65 @@ nodes: optional: true description: Nameplate channels this multiplexer is rated for, such as 8, 16, 40 or 96. Not a count in use. order_weight: 1300 + # Which grid this multiplexer is built for. A carrier reaches a channel through + # OtnMuxClientPort, whose `dwdm_channel` and `cwdm_channel` are both optional; + # this states which of the two a given device is supposed to use. Unenforced + # here, since a schema cannot compare a relationship against an attribute. + - name: plan + kind: Dropdown + default_value: dwdm + choices: + - name: dwdm + label: DWDM + description: ITU-T G.694.1 dense grid. + color: "#2196f3" + - name: cwdm + label: CWDM + description: ITU-T G.694.2 coarse plan. + color: "#ff9800" + optional: false + description: Which wavelength plan this multiplexer multiplexes. + order_weight: 1310 + # Gives a fixed OADM a home. A ROADM is its own kind, because it is switched + # rather than filtered; everything passive lives here and differs by role. + - name: mux_role + kind: Dropdown + default_value: mux_demux + choices: + - name: mux + label: Multiplexer + description: Combines channels onto one common port. + color: "#4caf50" + - name: demux + label: Demultiplexer + description: Splits one common port into channels. + color: "#00bcd4" + - name: mux_demux + label: Mux/demux + description: Both directions in one device. + color: "#2196f3" + - name: oadm + label: Fixed OADM + description: Drops and adds a fixed subset, passing the rest through. + color: "#9c27b0" + optional: false + description: What this passive device does with the channels it carries. + order_weight: 1320 + - name: technology + kind: Dropdown + choices: + - name: thin_film_filter + label: Thin-film filter + color: "#795548" + - name: awg + label: AWG + color: "#607d8b" + - name: interleaver + label: Interleaver + color: "#009688" + optional: true + description: How the device separates wavelengths. Nameplate data, often unknown. + order_weight: 1330 relationships: # The wavelengths a coarse multiplexer lights are a property of the device # rather than of the plan. @@ -2937,8 +3021,8 @@ extensions: order_weight: 930 # OTN records its pluggables on the library's transceiver kinds rather than a - # second inventory. `otn_port` is where the module is fitted, because no OTN port - # is a DcimEndpoint and `interface` cannot serve. The part catalog stays on the + # second inventory. `otn_port` is where the module is fitted, because `interface` + # peers InterfacePhysical and no OTN port is one. The part catalog stays on the # OTN side, since OtnTransceiverType references optical modes. - kind: DcimGenericTransceiver relationships: diff --git a/objects/extensions/otn/otn.yml b/objects/extensions/otn/otn.yml index b23a7c85..41cacf05 100644 --- a/objects/extensions/otn/otn.yml +++ b/objects/extensions/otn/otn.yml @@ -10,7 +10,7 @@ # # References use each kind's human_friendly_id: a device by name, a port by # device plus name. OtnFrequencyGrid is referenced with a nested kind/data -# block instead, because its hfid is a Number. +# block instead, because its hfid is a Number, the centre frequency in MHz. # --------------------------------------------------------------------------- --- apiVersion: infrahub.app/v1 @@ -40,9 +40,9 @@ kind: Object spec: kind: OtnFrequencyGrid data: - # ITU-T G.694.1 100 GHz grid, channel 21 at 192.1 THz. - - channel_number: 21 - center_frequency_mhz: 192100000 + # ITU-T G.694.1, 192.1 THz. On the 50 GHz plan this extension models, that is + # channel 16, which `channel_number` derives rather than storing. + - center_frequency_mhz: 192100000 --- apiVersion: infrahub.app/v1 @@ -65,7 +65,6 @@ spec: - name: nyc1-txp01 status: active role: edge - element_class: transponder vendor: Cisco model: NCS 1004 site: NYC1 @@ -93,7 +92,6 @@ spec: - name: sjc1-txp01 status: active role: edge - element_class: transponder vendor: Cisco model: NCS 1004 site: SJC1 @@ -142,7 +140,6 @@ spec: - name: nyc1-rdm01 status: active role: core - element_class: roadm vendor: Cisco model: NCS 2006 insertion_loss_mdb: 5500 @@ -155,8 +152,6 @@ spec: oper_state: up connector_type: LC polish: UPC - # The transponder line port patches into this add/drop port. - connected_to: [nyc1-txp01, line-1/0/0] - kind: OtnRoadmDegreePort data: name: degree-1 @@ -174,7 +169,6 @@ spec: - name: nyc1-mux01 status: active role: passive - element_class: mux_demux vendor: Cisco model: NCS1K-MD-64-C insertion_loss_mdb: 3500 @@ -190,7 +184,6 @@ spec: dwdm_channel: kind: OtnFrequencyGrid data: - channel_number: 21 center_frequency_mhz: 192100000 - kind: OtnMuxLinePort data: @@ -210,7 +203,6 @@ spec: # network is many spans with amplifiers between them. - name: nyc1-sjc1-span-1 description: Leased dark fiber between NYC1 and SJC1. - element_class: fiber_span length_m: 82000 insertion_loss_mdb: 15580 splice_count: 20 @@ -226,13 +218,12 @@ kind: Object spec: kind: OtnOpticalCarrier data: - - name: nyc1-sjc1-ch21 + - name: nyc1-sjc1-ch16 description: 400G wavelength on channel 21 between NYC1 and SJC1. status: active channel: kind: OtnFrequencyGrid data: - channel_number: 21 center_frequency_mhz: 192100000 line_ports: - [nyc1-txp01, line-1/0/0] @@ -260,7 +251,7 @@ spec: - name: nyc1-sjc1-path-1 description: Single segment carrying the wavelength from NYC1 to SJC1. segment_sequence: 1 - carrier: nyc1-sjc1-ch21 + carrier: nyc1-sjc1-ch16 hops: kind: OtnPathHop data: @@ -274,15 +265,38 @@ spec: data: name: nyc1-rdm01 role: core - element_class: roadm - name: nyc1-sjc1-hop-2 sequence: 2 element: kind: OtnFiberSpan data: name: nyc1-sjc1-span-1 - element_class: fiber_span length_m: 82000 fiber_type: smf-g652d site_a: NYC1 site_b: SJC1 + +--- +# The intra-site jumper from the transponder line port into the ROADM add/drop +# port. Every OTN port kind inherits DcimEndpoint, so extensions/cable records +# this run. `connected_endpoints` peers the DcimEndpoint generic, which has no +# hfid, so each end names its concrete kind and upserts against the port +# created above by device plus name. +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: DcimCable + data: + - label: nyc1-txp01-line-1/0/0--nyc1-rdm01-add-drop-1/1 + status: connected + cable_type: smf_os2 + length: 3 + connected_endpoints: + - kind: OtnLinePort + data: + name: line-1/0/0 + device: nyc1-txp01 + - kind: OtnRoadmAddDropPort + data: + name: add-drop-1/1 + device: nyc1-rdm01 From 931fb1837c85a7e92d3e38bbe7b43a7be1ef19c0 Mon Sep 17 00:00:00 2001 From: Iddo Date: Tue, 8 Sep 2026 09:49:36 +0200 Subject: [PATCH 28/31] feat(otn)!: record the ROADM cross-connect on each path hop An OtnPathHop now carries an `action` and an ingress and egress port, so a hop states what the element does to the carrier and across which port pair. On a ROADM that pair is the cross-connect, which the model could not express before: the path knew which elements the light crossed but not which degree or add/drop port it entered on and left by, and `add` and `drop` had no representation at all. Both ports are optional, so a hop through an element whose ports are not modelled still records the order of travel. The inverses live on OtnGenericPort as `hops_ingress` and `hops_egress`, so a ROADM's switching state reads from its own ports rather than only by walking every service's path. A hop pair is also now unique per path position, since `path` plus `sequence` is a uniqueness constraint. Replaces OtnService.service_profile with `service_type` on the wavelength, transport and IP transit axis. The old dropdown encoded one deployment's customer taxonomy, which does not belong in a shared library; `sla` and `max_latency_ns` already carry the commitments and `client_signal` and `containers` carry the client layer. Keeps OtnPatchPanel, which an earlier review had proposed removing. It is the optical face of a frame and the only way a panel can be a lossy hop on a path, so removing it would lose that. The terminations belong on the physical side instead, where extensions/patch_panel models the front and rear interfaces and the mapping between them, and a comment now says so and says to leave `ports` empty. BREAKING CHANGE: OtnService.service_profile is replaced by service_type, whose values are unrelated to the old ones. --- .metadata.yml | 11 +-- docs/docs/reference/otn.mdx | 124 +++++++++++++++++++++++++-------- extensions/otn/otn.yml | 114 ++++++++++++++++++++++++------ objects/extensions/otn/otn.yml | 9 ++- 4 files changed, 203 insertions(+), 55 deletions(-) diff --git a/.metadata.yml b/.metadata.yml index f8654d23..ea9f3ae7 100644 --- a/.metadata.yml +++ b/.metadata.yml @@ -231,9 +231,10 @@ extensions/otn: name: OTN not_covered: - Validation of what a schema cannot express, such as which port kind may hold - a pluggable, or whether a mux client port binds a channel on the same plan its - multiplexer declares. This extension ships no checks; the schema comments mark - each such rule. + a pluggable, whether a mux client port binds a channel on the same plan its + multiplexer declares, or whether a hop's ingress and egress ports belong to + that hop's own element. This extension ships no checks; the schema comments + mark each such rule. - Device types and platforms for optical gear. Model the physical asset as a DcimPhysicalDevice and link it with OtnGenericDevice.dcim_device. That edge is also the route to a rack elevation, since the physical record carries position, rack_face and @@ -263,7 +264,9 @@ extensions/otn: so a DcimCable terminates on one for intra-site jumpers, through a DcimPatchPanel where a panel sits in the run. - Provisioning a wavelength end to end, as a service with an optical path and - its hops through the elements the light crosses. + its hops through the elements the light crosses. Each hop records what the element + does to the carrier and across which port pair, so a ROADM cross-connect reads + either from the path or from the ROADM's own ports. extensions/patch_panel: dependencies: - base diff --git a/docs/docs/reference/otn.mdx b/docs/docs/reference/otn.mdx index 58ea5b5a..68eca013 100644 --- a/docs/docs/reference/otn.mdx +++ b/docs/docs/reference/otn.mdx @@ -18,10 +18,10 @@ This extension covers: - Tracking pluggable optics as inventory, with the part number in this extension's catalog, the serial on the library's transceiver kinds from extensions/transceiver, and which optical port each unit is fitted in. - Modelling the outside plant, including fiber types, conduits, spans and the optical multiplex sections built over them. A section terminates on any OMS endpoint, so a fixed point-to-point system built from passive multiplexers is modellable without a ROADM. - Cabling optical gear with extensions/cable. Every OTN port kind inherits DcimEndpoint, so a DcimCable terminates on one for intra-site jumpers, through a DcimPatchPanel where a panel sits in the run. -- Provisioning a wavelength end to end, as a service with an optical path and its hops through the elements the light crosses. +- Provisioning a wavelength end to end, as a service with an optical path and its hops through the elements the light crosses. Each hop records what the element does to the carrier and across which port pair, so a ROADM cross-connect reads either from the path or from the ROADM's own ports. Out of scope, and what to reach for instead: -- Validation of what a schema cannot express, such as which port kind may hold a pluggable, or whether a mux client port binds a channel on the same plan its multiplexer declares. This extension ships no checks; the schema comments mark each such rule. +- Validation of what a schema cannot express, such as which port kind may hold a pluggable, whether a mux client port binds a channel on the same plan its multiplexer declares, or whether a hop's ingress and egress ports belong to that hop's own element. This extension ships no checks; the schema comments mark each such rule. - Device types and platforms for optical gear. Model the physical asset as a DcimPhysicalDevice and link it with OtnGenericDevice.dcim_device. That edge is also the route to a rack elevation, since the physical record carries position, rack_face and a location that may be a LocationRack. Nothing keeps OtnGenericDevice.site in agreement with the site above that rack. - Optical performance monitoring. The schema carries ratings and settings, such as a port's transmit power or a mode's required OSNR, but nothing that is measured, such as live power, OSNR, pre-FEC BER or an error counter. Those change continuously and belong in the network management system or a time series store. - Optical path budgets. OtnOpticalPath records which elements the light crosses and in what order, not the loss, OSNR margin or latency of that route. Compute those in a planning tool such as GNPy. @@ -619,8 +619,8 @@ Out of scope, and what to reach for instead: | sla | | Dropdown | False | silver | gold, silver, bronze, best_effort | | status | | Dropdown | False | planned | planned, provisioning, active, maintenance, failed, rejected, decommissioned | | provisioned_date | When the circuit was handed to the customer. A recorded fact, not a reading. | DateTime | True | | | -| service_profile | | Dropdown | False | ip-transit | ai-training-dci, ai-inference, hpc-research, ip-transit, legacy-sdh | -| max_latency_ns | One-way latency budget in nanoseconds. Null on the profiles that have none. | Number | True | | | +| service_type | Which kind of service this is. | Dropdown | False | wavelength | wavelength, transport, ip_transit | +| max_latency_ns | One-way latency budget in nanoseconds. Null when the service commits to none. | Number | True | | | | max_latency_display | Latency budget in microseconds. Read-only, derived from max_latency_ns. | Text | True | | | #### Relationships @@ -689,6 +689,8 @@ Out of scope, and what to reach for instead: - **Description:** One ordered element on a path. - **Namespace:** Otn - **Icon:** mdi:ray-vertex +- **Uniqueness Constraints:** + - path, sequence__value - **Human Friendly ID:** name__value #### Attributes @@ -697,6 +699,7 @@ Out of scope, and what to reach for instead: | ---- | ----------- | ---- | -------- | ------------- | ------- | | name | | Text | False | | | | sequence | Position along the path, counting from the endpoint A ROADM. | Number | False | | | +| action | What this element does to the carrier at this hop. | Dropdown | True | | add, drop, express, through, terminate | #### Relationships @@ -704,6 +707,8 @@ Out of scope, and what to reach for instead: | ---- | ---- | -------- | ----------- | ---- | | path | OtnOpticalPath | False | one | Parent | | element | OtnOpticalElement | False | one | Attribute | +| ingress_port | OtnGenericPort | True | one | Attribute | +| egress_port | OtnGenericPort | True | one | Attribute | ### Facility @@ -754,6 +759,8 @@ Out of scope, and what to reach for instead: | name | peer | optional | cardinality | kind | | ---- | ---- | -------- | ----------- | ---- | | device | OtnGenericDevice | False | one | Parent | +| hops_ingress | OtnPathHop | True | many | Attribute | +| hops_egress | OtnPathHop | True | many | Attribute | ### OpticalElement @@ -1005,6 +1012,22 @@ generics: optional: false identifier: otn_device__ports order_weight: 800 + - name: hops_ingress + peer: OtnPathHop + label: Hops entering here + kind: Attribute + cardinality: many + optional: true + identifier: otn_hop__ingress_port + order_weight: 910 + - name: hops_egress + peer: OtnPathHop + label: Hops leaving here + kind: Attribute + cardinality: many + optional: true + identifier: otn_hop__egress_port + order_weight: 920 - name: OpticalElement namespace: Otn description: Anything light passes through and loses power in. @@ -2030,8 +2053,8 @@ nodes: read_only: true computed_attribute: kind: Jinja2 - jinja2_template: '{% if center_frequency_mhz__value is not none %}{{ ((center_frequency_mhz__value - - 191350000) // 50000) + 1 }}{% endif %}' + jinja2_template: '{% if center_frequency_mhz__value is not none -%} {{ ((center_frequency_mhz__value + - 191350000) // 50000) + 1 }} {%- endif %}' optional: true description: ITU channel number, 1 to 96. Channel 1 is 191.35 THz. Derived from the centre frequency. @@ -2060,8 +2083,8 @@ nodes: read_only: true computed_attribute: kind: Jinja2 - jinja2_template: '{% if center_frequency_mhz__value is not none %}{{ (299792458000 - / center_frequency_mhz__value) | round(2) }} nm{% endif %}' + jinja2_template: '{% if center_frequency_mhz__value is not none -%} {{ (299792458000 + / center_frequency_mhz__value) | round(2) }} nm {%- endif %}' optional: true description: Centre wavelength in nm, derived from the centre frequency. order_weight: 1520 @@ -2102,10 +2125,10 @@ nodes: read_only: true computed_attribute: kind: Jinja2 - jinja2_template: '{% if center_wavelength_nm__value is not none %}{% if center_wavelength_nm__value - < 1360 %}O{% elif center_wavelength_nm__value < 1460 %}E{% elif center_wavelength_nm__value - < 1530 %}S{% elif center_wavelength_nm__value < 1565 %}C{% else %}L{% endif - %}{% endif %}' + jinja2_template: '{% if center_wavelength_nm__value is not none -%} {% if center_wavelength_nm__value + < 1360 -%}O {%- elif center_wavelength_nm__value < 1460 -%}E {%- elif center_wavelength_nm__value + < 1530 -%}S {%- elif center_wavelength_nm__value < 1565 -%}C {%- else -%}L + {%- endif %} {%- endif %}' optional: true description: ITU band, derived from the wavelength. Only the two C-band wavelengths sit in the erbium window. @@ -3340,26 +3363,24 @@ nodes: description: When the circuit was handed to the customer. A recorded fact, not a reading. order_weight: 1520 - - name: service_profile + - name: service_type kind: Dropdown - default_value: ip-transit + default_value: wavelength choices: - - name: ai-training-dci - label: AI training DCI - color: '#e91e63' - - name: ai-inference - label: AI inference - color: '#9c27b0' - - name: hpc-research - label: HPC research - color: '#3f51b5' - - name: ip-transit - label: IP transit + - name: wavelength + label: Wavelength + description: A whole optical carrier handed over to the customer. color: '#2196f3' - - name: legacy-sdh - label: Legacy SDH - color: '#795548' + - name: transport + label: Transport + description: A sub-carrier circuit, groomed into ODU containers. + color: '#673ab7' + - name: ip_transit + label: IP transit + description: IP handoff, with the transport kept internal. + color: '#4caf50' optional: false + description: Which kind of service this is. order_weight: 1600 - name: max_latency_ns kind: Number @@ -3367,8 +3388,8 @@ nodes: min_value: 0 max_value: 1000000000 optional: true - description: One-way latency budget in nanoseconds. Null on the profiles that - have none. + description: One-way latency budget in nanoseconds. Null when the service commits + to none. order_weight: 1700 - name: max_latency_display kind: Text @@ -3562,6 +3583,9 @@ nodes: order_by: - sequence__value display_label: name__value + uniqueness_constraints: + - - path + - sequence__value attributes: - name: name kind: Text @@ -3579,6 +3603,32 @@ nodes: optional: false description: Position along the path, counting from the endpoint A ROADM. order_weight: 1100 + - name: action + kind: Dropdown + choices: + - name: add + label: Add + description: The carrier joins the line system here. + color: '#4caf50' + - name: drop + label: Drop + description: The carrier leaves the line system here. + color: '#ff9800' + - name: express + label: Express + description: Switched degree to degree without reaching an add/drop port. + color: '#673ab7' + - name: through + label: Through + description: Crossed passively, as on a span, an amplifier or a panel. + color: '#607d8b' + - name: terminate + label: Terminate + description: The light ends here, on a transponder or a router. + color: '#f44336' + optional: true + description: What this element does to the carrier at this hop. + order_weight: 1200 relationships: - name: path peer: OtnOpticalPath @@ -3594,6 +3644,20 @@ nodes: optional: false identifier: otn_hop__element order_weight: 810 + - name: ingress_port + peer: OtnGenericPort + kind: Attribute + cardinality: one + optional: true + identifier: otn_hop__ingress_port + order_weight: 820 + - name: egress_port + peer: OtnGenericPort + kind: Attribute + cardinality: one + optional: true + identifier: otn_hop__egress_port + order_weight: 830 - name: Facility namespace: Otn description: A supercomputing facility hosted at a PoP. diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index b6d21044..1238a77c 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -135,6 +135,25 @@ generics: optional: false identifier: otn_device__ports order_weight: 800 + # The inverses of OtnPathHop.ingress_port and .egress_port. Together they let a + # ROADM's switching state be read from its ports, rather than only by walking + # every service's path. + - name: hops_ingress + peer: OtnPathHop + label: Hops entering here + kind: Attribute + cardinality: many + optional: true + identifier: otn_hop__ingress_port + order_weight: 910 + - name: hops_egress + peer: OtnPathHop + label: Hops leaving here + kind: Attribute + cardinality: many + optional: true + identifier: otn_hop__egress_port + order_weight: 920 # No port-to-port adjacency attribute lives here. In the G.872 layering a # section owns its adjacency and a port only terminates it, so intra-site # jumpers are a DcimCable across the `connector` every port kind inherits from @@ -2179,6 +2198,11 @@ nodes: identifier: otn_mux_demux__cwdm_channels order_weight: 1900 + # The optical side of a frame, so a panel can be a lossy hop on a path. The + # terminations belong on the physical side: extensions/patch_panel models the + # front and rear interfaces and the mapping between them, and `dcim_device` joins + # the two records. This kind ships no port kind of its own for that reason, so + # leave `ports` empty rather than filling it with ports labelled for other roles. - name: PatchPanel namespace: Otn description: Optical distribution frame. Carries connector loss. @@ -2619,27 +2643,27 @@ nodes: optional: true description: When the circuit was handed to the customer. A recorded fact, not a reading. order_weight: 1520 - # The first three profiles carry a latency budget, the last two leave it null. - - name: service_profile + # What is sold, not who buys it. A customer-intent taxonomy belongs in the + # deployment that has those customers; `sla` and `max_latency_ns` carry the + # commitments, and `client_signal` and `containers` carry the client layer. + - name: service_type kind: Dropdown - default_value: ip-transit + default_value: wavelength choices: - - name: ai-training-dci - label: AI training DCI - color: "#e91e63" - - name: ai-inference - label: AI inference - color: "#9c27b0" - - name: hpc-research - label: HPC research - color: "#3f51b5" - - name: ip-transit - label: IP transit + - name: wavelength + label: Wavelength + description: A whole optical carrier handed over to the customer. color: "#2196f3" - - name: legacy-sdh - label: Legacy SDH - color: "#795548" + - name: transport + label: Transport + description: A sub-carrier circuit, groomed into ODU containers. + color: "#673ab7" + - name: ip_transit + label: IP transit + description: IP handoff, with the transport kept internal. + color: "#4caf50" optional: false + description: Which kind of service this is. order_weight: 1600 - name: max_latency_ns kind: Number @@ -2647,7 +2671,7 @@ nodes: min_value: 0 max_value: 1000000000 optional: true - description: One-way latency budget in nanoseconds. Null on the profiles that have none. + description: One-way latency budget in nanoseconds. Null when the service commits to none. order_weight: 1700 - name: max_latency_display kind: Text @@ -2881,6 +2905,8 @@ nodes: order_by: - sequence__value display_label: name__value + uniqueness_constraints: + - ["path", "sequence__value"] attributes: - name: name kind: Text @@ -2898,6 +2924,36 @@ nodes: optional: false description: Position along the path, counting from the endpoint A ROADM. order_weight: 1100 + # What the element does to the carrier here. `add` and `drop` are the two a + # port pair alone cannot tell apart, since both cross an add/drop port and a + # degree; the direction of travel is what differs. Optional, because a span or + # an amplifier is simply crossed and a deployment may not track the rest. + - name: action + kind: Dropdown + choices: + - name: add + label: Add + description: The carrier joins the line system here. + color: "#4caf50" + - name: drop + label: Drop + description: The carrier leaves the line system here. + color: "#ff9800" + - name: express + label: Express + description: Switched degree to degree without reaching an add/drop port. + color: "#673ab7" + - name: through + label: Through + description: Crossed passively, as on a span, an amplifier or a panel. + color: "#607d8b" + - name: terminate + label: Terminate + description: The light ends here, on a transponder or a router. + color: "#f44336" + optional: true + description: What this element does to the carrier at this hop. + order_weight: 1200 relationships: - name: path peer: OtnOpticalPath @@ -2906,8 +2962,7 @@ nodes: optional: false identifier: otn_path__hops order_weight: 800 - # One relationship covers ROADMs, amplifiers and fiber spans. No ingress or - # egress port beside it: the plant records no port-level adjacency to a span. + # One relationship covers ROADMs, amplifiers and fiber spans. - name: element peer: OtnOpticalElement kind: Attribute @@ -2915,6 +2970,25 @@ nodes: optional: false identifier: otn_hop__element order_weight: 810 + # The port pair the element carries the light across. On a ROADM this is the + # cross-connect: with `action` it states which degree or add/drop port the + # carrier entered on and which it left by. Both optional, so a hop through an + # element whose ports are not modelled still records the order of travel. + # Which ports belong to the hop's own element is unenforced here. + - name: ingress_port + peer: OtnGenericPort + kind: Attribute + cardinality: one + optional: true + identifier: otn_hop__ingress_port + order_weight: 820 + - name: egress_port + peer: OtnGenericPort + kind: Attribute + cardinality: one + optional: true + identifier: otn_hop__egress_port + order_weight: 830 # Location. diff --git a/objects/extensions/otn/otn.yml b/objects/extensions/otn/otn.yml index 41cacf05..a714205c 100644 --- a/objects/extensions/otn/otn.yml +++ b/objects/extensions/otn/otn.yml @@ -240,7 +240,7 @@ spec: rate_gbps: 400 sla: gold status: active - service_profile: ai-training-dci + service_type: wavelength max_latency_ns: 500000 endpoint_a: nyc1-txp01 endpoint_z: sjc1-txp01 @@ -258,15 +258,22 @@ spec: # `element` peers with the OtnOpticalElement generic, which has # no hfid, so each hop names the concrete kind and upserts # against the object created above by its mandatory fields. + # The ROADM cross-connect: the carrier is added, entering on the + # add/drop port the transponder patches into and leaving by the + # degree that faces SJC1. - name: nyc1-sjc1-hop-1 sequence: 1 + action: add element: kind: OtnRoadm data: name: nyc1-rdm01 role: core + ingress_port: [nyc1-rdm01, add-drop-1/1] + egress_port: [nyc1-rdm01, degree-1] - name: nyc1-sjc1-hop-2 sequence: 2 + action: through element: kind: OtnFiberSpan data: From f6e530693fe64094368e434471c43500d39fc5d0 Mon Sep 17 00:00:00 2001 From: Iddo Date: Tue, 8 Sep 2026 10:32:13 +0200 Subject: [PATCH 29/31] feat(otn): add OtnSplitter and a protected, passive and coarse scenario The demo data validated a forty-kind model with one straight-line service: one wavelength, one span, one path. Most of what the model can express went unexercised, including everything added on this branch. It now carries a second scenario built from the same two sites. A far-end ROADM at SJC1, so a wavelength has somewhere to be dropped and a section has two ends that can terminate it. Two conduits on separate routes and a second, longer span in the southern one, so diversity is a property of the plant rather than a label. Three multiplex sections, one of which runs between two passive coarse multiplexers with no ROADM at either end, which is the case OtnOmsEndpoint was added for. A two-stage amplifier chain on the north section, ordered by oms_sequence. A coarse pair carrying two CWDM wavelengths, whose bands derive from the wavelength. And the service now has a working path over the north route and a protect path over the south, both at segment position 1, distinguished only by path_role, which is what the third term of that uniqueness constraint is for. Each hop names its action and its port pair, so both routes read as cross-connect chains. Adds OtnSplitter and OtnSplitterPort. Optical line protection is built from a coupler, and the model had no way to express one: element_class carried a `splitter` value with no matching kind, and removing that attribute took the last trace of the concept with it. The split loss stays in insertion_loss_mdb, since a ratio alone does not give it. OtnAmplifier.oms_sequence becomes optional, matching OtnFiberSpan. Both oms_a2b and oms_b2a are optional, so an amplifier sitting in no chain should not have to claim a position in one. Records four omissions in .metadata.yml that a reader had to discover: protection mechanisms, the OTU and OPU layers, the absence of a record-of-design figure on a path, and that device types and rack elevations are reached through dcim_device rather than modelled natively. --- .metadata.yml | 30 ++- docs/docs/reference/otn.mdx | 111 +++++++++- extensions/otn/otn.yml | 82 ++++++- objects/extensions/otn/otn.yml | 385 ++++++++++++++++++++++++++++++++- 4 files changed, 592 insertions(+), 16 deletions(-) diff --git a/.metadata.yml b/.metadata.yml index ea9f3ae7..bf2d8b46 100644 --- a/.metadata.yml +++ b/.metadata.yml @@ -235,21 +235,32 @@ extensions/otn: multiplexer declares, or whether a hop's ingress and egress ports belong to that hop's own element. This extension ships no checks; the schema comments mark each such rule. - - Device types and platforms for optical gear. Model the physical asset as a DcimPhysicalDevice - and link it with OtnGenericDevice.dcim_device. That edge is also the route to - a rack elevation, since the physical record carries position, rack_face and - a location that may be a LocationRack. Nothing keeps OtnGenericDevice.site in - agreement with the site above that rack. + - Device types, platforms and rack elevations for optical gear, as native OTN + fields. Model the physical asset as a DcimPhysicalDevice and link it with OtnGenericDevice.dcim_device; + that edge reaches device_type, platform, position and rack_face, and a location + that may be a LocationRack. Nothing keeps OtnGenericDevice.site in agreement + with the site above that rack. - Optical performance monitoring. The schema carries ratings and settings, such as a port's transmit power or a mode's required OSNR, but nothing that is measured, such as live power, OSNR, pre-FEC BER or an error counter. Those change continuously and belong in the network management system or a time series store. - - Optical path budgets. OtnOpticalPath records which elements the light crosses - and in what order, not the loss, OSNR margin or latency of that route. Compute - those in a planning tool such as GNPy. + - Optical path budgets, including the record of design. OtnOpticalPath records + which elements the light crosses, in what order and across which ports, but + carries no loss, OSNR margin or latency for that route, so a turn-up figure + has nowhere to live. OtnService.max_latency_ns is a commitment on the service, + not a measurement of a path. Compute budgets in a planning tool such as GNPy. - Frequency plans other than one fixed dense grid. OtnFrequencyGrid models a single ITU-T G.694.1 fixed 50 GHz plan across the C band, from 191.35 to 196.10 THz. L band, C plus L systems, flexgrid and other channel spacings are out of scope. + - Protection mechanisms. OtnOpticalPath.path_role marks a path as working or protect + and OtnDiversityGroup keeps two services apart, but nothing states the scheme, + so 1+1, 1:1, optical line protection, OMS protection and ODU SNCP are indistinguishable. + Switching thresholds and hold-off timers are out of scope with it. OtnSplitter + models the coupler such a scheme is built from. + - The OTU and OPU layers of ITU-T G.709. The model goes from the optical channel, + OtnOpticalCarrier, straight to the ODU, OtnContainer. Forward error correction + lives on OtnOpticalMode, so what an OTU would add is its section monitoring + and framing overhead, which are not modelled. use_cases: - Recording the ITU-T G.694.1 dense grid and G.694.2 coarse plan as separate kinds, so a carrier pointed at the wrong plan is refused when written. @@ -267,6 +278,9 @@ extensions/otn: its hops through the elements the light crosses. Each hop records what the element does to the carrier and across which port pair, so a ROADM cross-connect reads either from the path or from the ROADM's own ports. + - Modelling optical protection, with a working and a protect path on one service, + a diversity group that keeps two services off shared plant, and OtnSplitter + for the coupler that feeds both legs. extensions/patch_panel: dependencies: - base diff --git a/docs/docs/reference/otn.mdx b/docs/docs/reference/otn.mdx index 68eca013..039c336a 100644 --- a/docs/docs/reference/otn.mdx +++ b/docs/docs/reference/otn.mdx @@ -19,13 +19,16 @@ This extension covers: - Modelling the outside plant, including fiber types, conduits, spans and the optical multiplex sections built over them. A section terminates on any OMS endpoint, so a fixed point-to-point system built from passive multiplexers is modellable without a ROADM. - Cabling optical gear with extensions/cable. Every OTN port kind inherits DcimEndpoint, so a DcimCable terminates on one for intra-site jumpers, through a DcimPatchPanel where a panel sits in the run. - Provisioning a wavelength end to end, as a service with an optical path and its hops through the elements the light crosses. Each hop records what the element does to the carrier and across which port pair, so a ROADM cross-connect reads either from the path or from the ROADM's own ports. +- Modelling optical protection, with a working and a protect path on one service, a diversity group that keeps two services off shared plant, and OtnSplitter for the coupler that feeds both legs. Out of scope, and what to reach for instead: - Validation of what a schema cannot express, such as which port kind may hold a pluggable, whether a mux client port binds a channel on the same plan its multiplexer declares, or whether a hop's ingress and egress ports belong to that hop's own element. This extension ships no checks; the schema comments mark each such rule. -- Device types and platforms for optical gear. Model the physical asset as a DcimPhysicalDevice and link it with OtnGenericDevice.dcim_device. That edge is also the route to a rack elevation, since the physical record carries position, rack_face and a location that may be a LocationRack. Nothing keeps OtnGenericDevice.site in agreement with the site above that rack. +- Device types, platforms and rack elevations for optical gear, as native OTN fields. Model the physical asset as a DcimPhysicalDevice and link it with OtnGenericDevice.dcim_device; that edge reaches device_type, platform, position and rack_face, and a location that may be a LocationRack. Nothing keeps OtnGenericDevice.site in agreement with the site above that rack. - Optical performance monitoring. The schema carries ratings and settings, such as a port's transmit power or a mode's required OSNR, but nothing that is measured, such as live power, OSNR, pre-FEC BER or an error counter. Those change continuously and belong in the network management system or a time series store. -- Optical path budgets. OtnOpticalPath records which elements the light crosses and in what order, not the loss, OSNR margin or latency of that route. Compute those in a planning tool such as GNPy. +- Optical path budgets, including the record of design. OtnOpticalPath records which elements the light crosses, in what order and across which ports, but carries no loss, OSNR margin or latency for that route, so a turn-up figure has nowhere to live. OtnService.max_latency_ns is a commitment on the service, not a measurement of a path. Compute budgets in a planning tool such as GNPy. - Frequency plans other than one fixed dense grid. OtnFrequencyGrid models a single ITU-T G.694.1 fixed 50 GHz plan across the C band, from 191.35 to 196.10 THz. L band, C plus L systems, flexgrid and other channel spacings are out of scope. +- Protection mechanisms. OtnOpticalPath.path_role marks a path as working or protect and OtnDiversityGroup keeps two services apart, but nothing states the scheme, so 1+1, 1:1, optical line protection, OMS protection and ODU SNCP are indistinguishable. Switching thresholds and hold-off timers are out of scope with it. OtnSplitter models the coupler such a scheme is built from. +- The OTU and OPU layers of ITU-T G.709. The model goes from the optical channel, OtnOpticalCarrier, straight to the ODU, OtnContainer. Forward error correction lives on OtnOpticalMode, so what an OTU would add is its section monitoring and framing overhead, which are not modelled. ## Nodes @@ -103,6 +106,20 @@ Out of scope, and what to reach for instead: - **Icon:** mdi:amplifier - **Inherit From:** DcimEndpoint, OtnGenericPort, OtnOpticalPort +### SplitterPort + +- **Label:** Splitter port +- **Description:** The common side of a splitter, or one of its output legs. +- **Namespace:** Otn +- **Icon:** mdi:call-split +- **Inherit From:** DcimEndpoint, OtnGenericPort, OtnOpticalPort + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| leg | Whether this port is the common side or an output leg. | Dropdown | False | output | common, output | + ### MuxClientPort - **Label:** Mux client port @@ -452,7 +469,7 @@ Out of scope, and what to reach for instead: | noise_figure_display | Noise figure in dB. Read-only, derived from noise_figure_mdb. | Text | True | | | | gain_mdb | Gain in millidecibels. 22.0 dB is 22000. Must cover the loss ahead of the input. | Number | False | 22000 | | | gain_display | Gain in dB. Read-only, derived from gain_mdb. | Text | True | | | -| oms_sequence | Position in this amplifier's own chain, counting along the direction it amplifies. | Number | False | | | +| oms_sequence | Position in this amplifier's own chain, counting along the direction it amplifies. | Number | True | | | | max_total_output_power_mdbm | Rated total output power in milli-dBm, 0 to +30 dBm. A nameplate ceiling, not a reading. | Number | True | | | | max_total_output_power_display | Rated total output power in dBm. Read-only, derived from max_total_output_power_mdbm. | Text | True | | | @@ -538,6 +555,22 @@ Out of scope, and what to reach for instead: | ---- | ---- | -------- | ----------- | ---- | | carriers | OtnOpticalCarrier | True | many | Attribute | +### Splitter + +- **Label:** Splitter +- **Description:** Passive splitter or coupler. Divides one carrier across several legs. +- **Namespace:** Otn +- **Icon:** mdi:call-split +- **Inherit From:** OtnGenericDevice, OtnOpticalElement + +#### Attributes + +| name | description | kind | optional | default_value | choices | +| ---- | ----------- | ---- | -------- | ------------- | ------- | +| split_type | How the input is divided across the output legs. | Dropdown | False | even | even, tap | +| output_count | Number of output legs, such as 2 for a 1:2 or 8 for a 1:8. | Number | False | 2 | | +| tap_ratio_percent | Share taken by the tap leg, as a percentage. 10 is a 90/10 tap. Null on an even split. | Number | True | | | + ### FixedAttenuator - **Label:** Fixed attenuator @@ -1475,6 +1508,32 @@ nodes: - DcimEndpoint - OtnGenericPort - OtnOpticalPort +- name: SplitterPort + namespace: Otn + description: The common side of a splitter, or one of its output legs. + label: Splitter port + icon: mdi:call-split + include_in_menu: false + inherit_from: + - DcimEndpoint + - OtnGenericPort + - OtnOpticalPort + attributes: + - name: leg + kind: Dropdown + default_value: output + choices: + - name: common + label: Common + description: The single side that carries the whole carrier. + color: '#2196f3' + - name: output + label: Output + description: One of the divided legs. + color: '#4caf50' + optional: false + description: Whether this port is the common side or an output leg. + order_weight: 1500 - name: MuxClientPort namespace: Otn description: One channel of a multiplexer. The side facing the transponder or router @@ -2843,7 +2902,7 @@ nodes: parameters: min_value: 1 max_value: 51 - optional: false + optional: true description: Position in this amplifier's own chain, counting along the direction it amplifies. order_weight: 1560 @@ -3084,6 +3143,50 @@ nodes: optional: true identifier: otn_odu_switch__carriers order_weight: 1900 +- name: Splitter + namespace: Otn + description: Passive splitter or coupler. Divides one carrier across several legs. + label: Splitter + icon: mdi:call-split + include_in_menu: true + menu_placement: OtnGenericDevice + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + attributes: + - name: split_type + kind: Dropdown + default_value: even + choices: + - name: even + label: Even + description: Every output leg receives the same share. + color: '#2196f3' + - name: tap + label: Tap + description: One leg takes a small share for monitoring, the rest passes on. + color: '#ff9800' + optional: false + description: How the input is divided across the output legs. + order_weight: 1900 + - name: output_count + kind: Number + default_value: 2 + parameters: + min_value: 2 + max_value: 64 + optional: false + description: Number of output legs, such as 2 for a 1:2 or 8 for a 1:8. + order_weight: 1910 + - name: tap_ratio_percent + kind: Number + parameters: + min_value: 1 + max_value: 50 + optional: true + description: Share taken by the tap leg, as a percentage. 10 is a 90/10 tap. Null + on an even split. + order_weight: 1920 - name: FixedAttenuator namespace: Otn description: A pad. One fixed amount of loss, patched into a link that arrives too diff --git a/extensions/otn/otn.yml b/extensions/otn/otn.yml index 1238a77c..b9e8cf8b 100644 --- a/extensions/otn/otn.yml +++ b/extensions/otn/otn.yml @@ -644,6 +644,35 @@ nodes: - OtnGenericPort - OtnOpticalPort + - name: SplitterPort + namespace: Otn + description: The common side of a splitter, or one of its output legs. + label: Splitter port + icon: mdi:call-split + include_in_menu: false + inherit_from: + - DcimEndpoint + - OtnGenericPort + - OtnOpticalPort + attributes: + # Which side of the split this port is on. `role` on OtnGenericPort names what + # the port is for in the network; this names its place inside the device. + - name: leg + kind: Dropdown + default_value: output + choices: + - name: common + label: Common + description: The single side that carries the whole carrier. + color: "#2196f3" + - name: output + label: Output + description: One of the divided legs. + color: "#4caf50" + optional: false + description: Whether this port is the common side or an output leg. + order_weight: 1500 + # One multiplexer channel and the common side it shares. Neither declares an # insertion loss: that belongs to the device, so a per-port figure would double # count. @@ -2067,7 +2096,10 @@ nodes: max_value: 51 # No default_value on purpose: a default of 1 would let a new amplifier take # a silent position at the head of its chain and sort into a wrong answer. - optional: false + # Optional for the same reason oms_sequence is optional on OtnFiberSpan: both + # oms_a2b and oms_b2a are optional, so an amplifier that sits in no chain yet + # should not have to claim a position in one. + optional: true description: Position in this amplifier's own chain, counting along the direction it amplifies. order_weight: 1560 - name: max_total_output_power_mdbm @@ -2345,6 +2377,54 @@ nodes: # Two attenuator kinds rather than one carrying a type Dropdown and an optional # range: a pad with a maximum is a field that should not exist. Neither declares # ports; both inherit `ports` from OtnGenericDevice and hold none. + # A passive coupler, used to feed a working and a protect leg from one carrier in + # optical line protection, and to tap a small fraction off for monitoring. The + # split loss itself belongs in insertion_loss_mdb from OtnOpticalElement, since a + # ratio alone does not give it: an even two-way split costs about 3.5 dB. + - name: Splitter + namespace: Otn + description: Passive splitter or coupler. Divides one carrier across several legs. + label: Splitter + icon: mdi:call-split + include_in_menu: true + menu_placement: OtnGenericDevice + inherit_from: + - OtnGenericDevice + - OtnOpticalElement + attributes: + - name: split_type + kind: Dropdown + default_value: even + choices: + - name: even + label: Even + description: Every output leg receives the same share. + color: "#2196f3" + - name: tap + label: Tap + description: One leg takes a small share for monitoring, the rest passes on. + color: "#ff9800" + optional: false + description: How the input is divided across the output legs. + order_weight: 1900 + - name: output_count + kind: Number + default_value: 2 + parameters: + min_value: 2 + max_value: 64 + optional: false + description: Number of output legs, such as 2 for a 1:2 or 8 for a 1:8. + order_weight: 1910 + - name: tap_ratio_percent + kind: Number + parameters: + min_value: 1 + max_value: 50 + optional: true + description: Share taken by the tap leg, as a percentage. 10 is a 90/10 tap. Null on an even split. + order_weight: 1920 + - name: FixedAttenuator namespace: Otn description: A pad. One fixed amount of loss, patched into a link that arrives too hot. diff --git a/objects/extensions/otn/otn.yml b/objects/extensions/otn/otn.yml index a714205c..f10cfbd2 100644 --- a/objects/extensions/otn/otn.yml +++ b/objects/extensions/otn/otn.yml @@ -159,6 +159,15 @@ spec: oper_state: up connector_type: LC polish: UPC + direction: west + - kind: OtnRoadmDegreePort + data: + name: degree-2 + role: degree + oper_state: up + connector_type: LC + polish: UPC + direction: south --- apiVersion: infrahub.app/v1 @@ -193,6 +202,189 @@ spec: connector_type: LC polish: UPC +--- +# Two conduits on separate routes. Diversity is a property of the plant, so the +# protect path earns its name by riding a span in the other one. +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnConduit + data: + - name: nyc1-sjc1-conduit-north + description: Northern duct route between NYC1 and SJC1. + owner: Lumen + - name: nyc1-sjc1-conduit-south + description: Southern duct route between NYC1 and SJC1. + owner: Colt + +--- +# The far-end ROADM. Without it the wavelength had nowhere to be dropped, and a +# section had only one end that could terminate it. +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnRoadm + data: + - name: sjc1-rdm01 + status: active + role: core + vendor: Cisco + model: NCS 2006 + insertion_loss_mdb: 5500 + technology: wss_lcos + channel_capacity: 96 + colorless: true + directionless: true + contentionless: false + site: SJC1 + ports: + - kind: OtnRoadmAddDropPort + data: + name: add-drop-1/1 + role: add_drop + oper_state: up + connector_type: LC + polish: UPC + - kind: OtnRoadmDegreePort + data: + name: degree-1 + role: degree + oper_state: up + connector_type: LC + polish: UPC + direction: east + - kind: OtnRoadmDegreePort + data: + name: degree-2 + role: degree + oper_state: up + connector_type: LC + polish: UPC + direction: north + +--- +# A fixed coarse pair, with no ROADM at either end. The section between these two +# is what OtnOmsEndpoint exists for: before it, a passive point-to-point system +# could not carry a multiplex section at all. +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnMuxDemux + data: + - name: nyc1-cwdm01 + status: active + role: passive + vendor: Cisco + model: NCS1K-MD-CWDM + insertion_loss_mdb: 2800 + plan: cwdm + mux_role: mux_demux + technology: thin_film_filter + channel_capacity: 8 + site: NYC1 + cwdm_channels: + kind: OtnCwdmChannel + data: + - center_wavelength_nm: 1471 + - center_wavelength_nm: 1491 + ports: + - kind: OtnMuxClientPort + data: + name: cwdm-client-1471 + role: client + oper_state: up + connector_type: LC + polish: UPC + cwdm_channel: + kind: OtnCwdmChannel + data: + center_wavelength_nm: 1471 + - kind: OtnMuxLinePort + data: + name: cwdm-line-1 + role: line + oper_state: up + connector_type: LC + polish: UPC + - name: sjc1-cwdm01 + status: active + role: passive + vendor: Cisco + model: NCS1K-MD-CWDM + insertion_loss_mdb: 2800 + plan: cwdm + mux_role: mux_demux + technology: thin_film_filter + channel_capacity: 8 + site: SJC1 + cwdm_channels: + kind: OtnCwdmChannel + data: + - center_wavelength_nm: 1471 + - center_wavelength_nm: 1491 + ports: + - kind: OtnMuxClientPort + data: + name: cwdm-client-1471 + role: client + oper_state: up + connector_type: LC + polish: UPC + cwdm_channel: + kind: OtnCwdmChannel + data: + center_wavelength_nm: 1471 + - kind: OtnMuxLinePort + data: + name: cwdm-line-1 + role: line + oper_state: up + connector_type: LC + polish: UPC + +--- +# The coupler that feeds the working and the protect leg. The scheme it serves, +# 1+1 or 1:1, is not modelled; see the not_covered note on protection mechanisms. +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnSplitter + data: + - name: nyc1-olp01 + status: active + role: passive + vendor: Cisco + model: NCS1K-OLP + split_type: even + output_count: 2 + insertion_loss_mdb: 3500 + site: NYC1 + ports: + - kind: OtnSplitterPort + data: + name: common + leg: common + role: line + oper_state: up + connector_type: LC + polish: UPC + - kind: OtnSplitterPort + data: + name: out-1 + leg: output + role: line + oper_state: up + connector_type: LC + polish: UPC + - kind: OtnSplitterPort + data: + name: out-2 + leg: output + role: line + oper_state: up + connector_type: LC + polish: UPC + --- apiVersion: infrahub.app/v1 kind: Object @@ -202,15 +394,145 @@ spec: # A single 82 km span stands in for the NYC1 to SJC1 route, which in a real # network is many spans with amplifiers between them. - name: nyc1-sjc1-span-1 - description: Leased dark fiber between NYC1 and SJC1. + description: Leased dark fiber between NYC1 and SJC1, north route. length_m: 82000 insertion_loss_mdb: 15580 splice_count: 20 fiber_type: smf-g652d site_a: NYC1 site_b: SJC1 + conduit: nyc1-sjc1-conduit-north + oms_sequence: 1 terminating_ports: - [nyc1-rdm01, degree-1] + - [sjc1-rdm01, degree-1] + # The diverse route the protect path takes. Longer, so its budget differs, and in + # a different conduit, which is what makes the two paths genuinely diverse. + - name: nyc1-sjc1-span-2 + description: Leased dark fiber between NYC1 and SJC1, south route. + length_m: 104000 + insertion_loss_mdb: 19760 + splice_count: 26 + fiber_type: smf-g652d + site_a: NYC1 + site_b: SJC1 + conduit: nyc1-sjc1-conduit-south + oms_sequence: 1 + terminating_ports: + - [nyc1-rdm01, degree-2] + - [sjc1-rdm01, degree-2] + # The passive CWDM tail. Its own span, because a span belongs to one section. + - name: nyc1-sjc1-span-3 + description: Dark fiber carrying the coarse tail between NYC1 and SJC1. + length_m: 41000 + insertion_loss_mdb: 8200 + splice_count: 9 + fiber_type: smf-g652d + site_a: NYC1 + site_b: SJC1 + oms_sequence: 1 + terminating_ports: + - [nyc1-cwdm01, cwdm-line-1] + - [sjc1-cwdm01, cwdm-line-1] + +--- +# Three sections. Two ROADM to ROADM on the diverse routes, and one between the +# passive coarse pair, which is only expressible because a section terminates on +# any OtnOmsEndpoint rather than on a ROADM. +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnOpticalMultiplexSection + data: + # `endpoint_a` and `endpoint_b` peer the OtnOmsEndpoint generic, which declares + # no hfid, so each end names its concrete kind and upserts by its own fields. + - name: nyc1-sjc1-oms-north + description: North route section, amplified in the NYC1 to SJC1 direction. + endpoint_a: + kind: OtnRoadm + data: + name: nyc1-rdm01 + role: core + endpoint_b: + kind: OtnRoadm + data: + name: sjc1-rdm01 + role: core + spans: + - nyc1-sjc1-span-1 + - name: nyc1-sjc1-oms-south + description: South route section, unamplified. + endpoint_a: + kind: OtnRoadm + data: + name: nyc1-rdm01 + role: core + endpoint_b: + kind: OtnRoadm + data: + name: sjc1-rdm01 + role: core + spans: + - nyc1-sjc1-span-2 + - name: nyc1-sjc1-oms-cwdm + description: Coarse section between two passive multiplexers, no ROADM at either end. + endpoint_a: + kind: OtnMuxDemux + data: + name: nyc1-cwdm01 + role: passive + endpoint_b: + kind: OtnMuxDemux + data: + name: sjc1-cwdm01 + role: passive + spans: + - nyc1-sjc1-span-3 + +--- +# The amplifier chain on the north section, in the A to B direction only. Position 1 +# is the booster leaving NYC1 and position 2 the pre-amplifier arriving at SJC1, which +# is what oms_sequence orders. The return direction is left unamplified here. +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnAmplifier + data: + - name: nyc1-amp01 + status: active + role: core + vendor: Cisco + model: NCS1K-EDFA + amplifier_type: edfa + stage: booster + gain_mdb: 17000 + noise_figure_mdb: 5000 + max_total_output_power_mdbm: 23000 + site: NYC1 + oms_a2b: nyc1-sjc1-oms-north + oms_sequence: 1 + - name: sjc1-amp01 + status: active + role: core + vendor: Cisco + model: NCS1K-EDFA + amplifier_type: edfa + stage: preamp + gain_mdb: 21000 + noise_figure_mdb: 4500 + max_total_output_power_mdbm: 20000 + site: SJC1 + oms_a2b: nyc1-sjc1-oms-north + oms_sequence: 2 + +--- +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnDiversityGroup + data: + - name: dg-lumen-east + description: Services that must not share plant with each other. --- apiVersion: infrahub.app/v1 @@ -219,7 +541,7 @@ spec: kind: OtnOpticalCarrier data: - name: nyc1-sjc1-ch16 - description: 400G wavelength on channel 21 between NYC1 and SJC1. + description: 400G wavelength on channel 16 between NYC1 and SJC1. status: active channel: kind: OtnFrequencyGrid @@ -245,12 +567,14 @@ spec: endpoint_a: nyc1-txp01 endpoint_z: sjc1-txp01 customer: Lumen + diversity_group: dg-lumen-east optical_path: kind: OtnOpticalPath data: - name: nyc1-sjc1-path-1 - description: Single segment carrying the wavelength from NYC1 to SJC1. + description: Working route, north, carrying the wavelength from NYC1 to SJC1. segment_sequence: 1 + path_role: working carrier: nyc1-sjc1-ch16 hops: kind: OtnPathHop @@ -282,6 +606,61 @@ spec: fiber_type: smf-g652d site_a: NYC1 site_b: SJC1 + # The far end of the working route, where the carrier leaves the line + # system. `drop` is the mirror of hop 1: same kind of port pair, other + # direction of travel, which is the distinction `action` records. + - name: nyc1-sjc1-hop-3 + sequence: 3 + action: drop + element: + kind: OtnRoadm + data: + name: sjc1-rdm01 + role: core + ingress_port: [sjc1-rdm01, degree-1] + egress_port: [sjc1-rdm01, add-drop-1/1] + # The protect route, south. It shares the service and the segment position + # with the working path and differs only in path_role, which is the third + # term of the uniqueness constraint that lets both exist. + - name: nyc1-sjc1-path-2 + description: Protect route, south, over the diverse conduit. + segment_sequence: 1 + path_role: protect + carrier: nyc1-sjc1-ch16 + hops: + kind: OtnPathHop + data: + - name: nyc1-sjc1-protect-hop-1 + sequence: 1 + action: add + element: + kind: OtnRoadm + data: + name: nyc1-rdm01 + role: core + ingress_port: [nyc1-rdm01, add-drop-1/1] + egress_port: [nyc1-rdm01, degree-2] + - name: nyc1-sjc1-protect-hop-2 + sequence: 2 + action: through + element: + kind: OtnFiberSpan + data: + name: nyc1-sjc1-span-2 + length_m: 104000 + fiber_type: smf-g652d + site_a: NYC1 + site_b: SJC1 + - name: nyc1-sjc1-protect-hop-3 + sequence: 3 + action: drop + element: + kind: OtnRoadm + data: + name: sjc1-rdm01 + role: core + ingress_port: [sjc1-rdm01, degree-2] + egress_port: [sjc1-rdm01, add-drop-1/1] --- # The intra-site jumper from the transponder line port into the ROADM add/drop From e1f0a3db0ab6e3e48003b18bcae5a180b36f4b14 Mon Sep 17 00:00:00 2001 From: Iddo Date: Tue, 8 Sep 2026 11:09:53 +0200 Subject: [PATCH 30/31] feat(otn): rack the ROADMs and name real optical vendors in the sample Both ROADMs now carry a physical record through dcim_device: a DcimDevice at a rack unit on a LocationRack, with a DcimDeviceType. The extension documents that route as how device types and rack elevations are reached, and nothing in the sample data demonstrated it. LocationRack and DcimDeviceType are created here rather than added to this extension's dependencies, which is what nine other extensions' object files already do for LocationRack. The sample also labelled every optical device Cisco. Cisco does build ROADMs, in the NCS 2000 line, but an all-one-vendor network reads as placeholder data. The line system, transponders, amplifiers and dense multiplexer are Ciena, the passive coarse pair and the protection coupler are Adtran, and Cisco keeps the grey client pluggable, where a third-party optic is ordinary. Names stay at platform granularity rather than inventing card codes, and the dense multiplexer's channel_capacity now matches the CMD44 it names. --- objects/extensions/otn/otn.yml | 123 ++++++++++++++++++++++++++------- 1 file changed, 98 insertions(+), 25 deletions(-) diff --git a/objects/extensions/otn/otn.yml b/objects/extensions/otn/otn.yml index f10cfbd2..2f1487d6 100644 --- a/objects/extensions/otn/otn.yml +++ b/objects/extensions/otn/otn.yml @@ -1,17 +1,37 @@ # --------------------------------------------------------------------------- # Mock data for extensions/otn. -# Depends on: base (OrganizationManufacturer: Cisco, OrganizationProvider: Lumen), -# extensions/location_minimal (LocationSite: NYC1, SJC1), extensions/transceiver -# (DcimStandardTransceiver, which records the fitted pluggable). +# Depends on: base (OrganizationManufacturer: Cisco, OrganizationProvider: Lumen +# and Colt), extensions/location_minimal (LocationSite: NYC1, SJC1), +# extensions/transceiver (DcimStandardTransceiver, which records the fitted +# pluggable). LocationRack and DcimDeviceType are created here rather than +# declared as a dependency, which is what the other extensions' object files do. # -# One facility, one span, one wavelength and one service end to end. Enough to -# walk the model from a service down to the ports it lands on, not a +# Two sites, three spans in two conduits, and one protected wavelength beside a +# passive coarse tail. Enough to walk the model from a service down to the ports +# it lands on and back up from a port to the ROADM switching it, not a # representative network. # +# The line system is Ciena and the transponders sit on it, with a passive coarse +# pair from Adtran and a Cisco grey client pluggable. Real platform names, at +# platform granularity rather than invented card codes. +# # References use each kind's human_friendly_id: a device by name, a port by # device plus name. OtnFrequencyGrid is referenced with a nested kind/data # block instead, because its hfid is a Number, the centre frequency in MHz. # --------------------------------------------------------------------------- +--- +# The optical vendors this sample uses. base ships Cisco only, which stays on the +# grey client pluggable, where a third-party optic is ordinary. +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OrganizationManufacturer + data: + - name: Ciena + description: Ciena Corporation. + - name: Adtran + description: Adtran Holdings, Inc., which now carries the ADVA FSP line. + --- apiVersion: infrahub.app/v1 kind: Object @@ -65,8 +85,8 @@ spec: - name: nyc1-txp01 status: active role: edge - vendor: Cisco - model: NCS 1004 + vendor: Ciena + model: Waveserver 5 site: NYC1 ports: - kind: OtnClientPort @@ -92,8 +112,8 @@ spec: - name: sjc1-txp01 status: active role: edge - vendor: Cisco - model: NCS 1004 + vendor: Ciena + model: Waveserver 5 site: SJC1 ports: - kind: OtnLinePort @@ -140,10 +160,36 @@ spec: - name: nyc1-rdm01 status: active role: core - vendor: Cisco - model: NCS 2006 + vendor: Ciena + model: 6500-T12 insertion_loss_mdb: 5500 site: NYC1 + # The physical asset for the same box. `dcim_device` peers the + # DcimPhysicalDevice generic, which declares no hfid, so the record names its + # concrete kind. It is the route to a rack elevation and to a device type, which + # this extension reaches on the Dcim side rather than modelling natively: + # `position` and `rack_face` sit on the record and its `location` is a + # LocationRack. The name is shared with the OTN device on purpose, since both + # records describe one box. + dcim_device: + kind: DcimDevice + data: + name: nyc1-rdm01 + position: 14 + rack_face: front + device_type: + kind: DcimDeviceType + data: + name: 6500-T12 + manufacturer: Ciena + location: + kind: LocationRack + data: + name: NYC1-RACK-01 + site: + kind: LocationSite + data: + name: NYC1 ports: - kind: OtnRoadmAddDropPort data: @@ -178,9 +224,10 @@ spec: - name: nyc1-mux01 status: active role: passive - vendor: Cisco - model: NCS1K-MD-64-C + vendor: Ciena + model: 6500 CMD44 insertion_loss_mdb: 3500 + channel_capacity: 44 site: NYC1 ports: - kind: OtnMuxClientPort @@ -228,8 +275,8 @@ spec: - name: sjc1-rdm01 status: active role: core - vendor: Cisco - model: NCS 2006 + vendor: Ciena + model: 6500-T12 insertion_loss_mdb: 5500 technology: wss_lcos channel_capacity: 96 @@ -237,6 +284,32 @@ spec: directionless: true contentionless: false site: SJC1 + # The physical asset for the same box. `dcim_device` peers the + # DcimPhysicalDevice generic, which declares no hfid, so the record names its + # concrete kind. It is the route to a rack elevation and to a device type, which + # this extension reaches on the Dcim side rather than modelling natively: + # `position` and `rack_face` sit on the record and its `location` is a + # LocationRack. The name is shared with the OTN device on purpose, since both + # records describe one box. + dcim_device: + kind: DcimDevice + data: + name: sjc1-rdm01 + position: 14 + rack_face: front + device_type: + kind: DcimDeviceType + data: + name: 6500-T12 + manufacturer: Ciena + location: + kind: LocationRack + data: + name: SJC1-RACK-01 + site: + kind: LocationSite + data: + name: SJC1 ports: - kind: OtnRoadmAddDropPort data: @@ -274,8 +347,8 @@ spec: - name: nyc1-cwdm01 status: active role: passive - vendor: Cisco - model: NCS1K-MD-CWDM + vendor: Adtran + model: FSP 3000 insertion_loss_mdb: 2800 plan: cwdm mux_role: mux_demux @@ -309,8 +382,8 @@ spec: - name: sjc1-cwdm01 status: active role: passive - vendor: Cisco - model: NCS1K-MD-CWDM + vendor: Adtran + model: FSP 3000 insertion_loss_mdb: 2800 plan: cwdm mux_role: mux_demux @@ -353,8 +426,8 @@ spec: - name: nyc1-olp01 status: active role: passive - vendor: Cisco - model: NCS1K-OLP + vendor: Adtran + model: FSP 3000 split_type: even output_count: 2 insertion_loss_mdb: 3500 @@ -501,8 +574,8 @@ spec: - name: nyc1-amp01 status: active role: core - vendor: Cisco - model: NCS1K-EDFA + vendor: Ciena + model: 6500 SLA amplifier_type: edfa stage: booster gain_mdb: 17000 @@ -514,8 +587,8 @@ spec: - name: sjc1-amp01 status: active role: core - vendor: Cisco - model: NCS1K-EDFA + vendor: Ciena + model: 6500 SLA amplifier_type: edfa stage: preamp gain_mdb: 21000 From b87f54299326c5f606e6a677431d9db66de93732 Mon Sep 17 00:00:00 2001 From: Iddo Date: Tue, 8 Sep 2026 12:00:32 +0200 Subject: [PATCH 31/31] feat(otn): groom a client signal into ODU containers in the sample The ODU and container layer was the part of the model that nothing in the sample data touched, which left the deepest part of G.709 described but never loaded. The 400G wavelength now carries an ODUC4 offering 320 tributary slots, with one ODU4 taking a 100GbE client directly by GMP and a second ODU4 multiplexing a lower-order ODU2 that carries an STM-64 by BMP. Slot counts are the G.709 figures the schema descriptions cite: an ODU4 takes 80 slots in an ODUC4 and offers 80 to its own children, an ODU2 takes 8 of those. Parent and child edges are exercised two levels deep, which is what the directional self-relationship on OtnContainer is for. The service becomes `transport` and names its headline client signal. A service whose payload is groomed into containers is not a whole lambda handed over, so `wavelength` contradicted its own data. Also fills the last of the untouched relationships: the carrier names its optical mode and both sections it traverses, the north section gains its return-direction amplifier chain on `oms_b2a`, a distributed Raman pump sits on the long south span, an ODU cross-connect switches the carrier at the far end, and a coherent 400ZR pluggable joins the catalogue so a transceiver type has a mode to declare. Two optical modes carry the vendor figures for a transponder line side and for 400ZR. Sample data now exercises all 42 relationship identifiers this extension declares, up from 30. The client signal catalogue loads ahead of the service that references it, since object specs resolve in file order. --- objects/extensions/otn/otn.yml | 209 ++++++++++++++++++++++++++++++++- 1 file changed, 203 insertions(+), 6 deletions(-) diff --git a/objects/extensions/otn/otn.yml b/objects/extensions/otn/otn.yml index 2f1487d6..6758a8a2 100644 --- a/objects/extensions/otn/otn.yml +++ b/objects/extensions/otn/otn.yml @@ -6,10 +6,11 @@ # pluggable). LocationRack and DcimDeviceType are created here rather than # declared as a dependency, which is what the other extensions' object files do. # -# Two sites, three spans in two conduits, and one protected wavelength beside a -# passive coarse tail. Enough to walk the model from a service down to the ports -# it lands on and back up from a port to the ROADM switching it, not a -# representative network. +# Two sites, three spans in two conduits, and one protected 400G transport service +# beside a passive coarse tail. Its payload is groomed two levels deep into ODU +# containers. Enough to walk the model from a service down to the ports it lands on, +# back up from a port to the ROADM switching it, and down again from a wavelength to +# the client signals inside it. Not a representative network. # # The line system is Ciena and the transponders sit on it, with a passive coarse # pair from Adtran and a Cisco grey client pluggable. Real platform names, at @@ -64,6 +65,64 @@ spec: # channel 16, which `channel_number` derives rather than storing. - center_frequency_mhz: 192100000 +--- +# The client signals this network grooms. Rates are the nominal line rates from +# G.709 and G.707, in kbps, and `default_container_type` is the first step of the +# mapping chain rather than the last. +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnClientSignal + data: + - name: 100GBASE-LR4 + description: 100 Gigabit Ethernet over four wavelengths, 10 km. + layer: ethernet + auto_selectable: true + bit_rate_kbps: 103100000 + default_container_type: ODU4 + default_mapping: GMP + - name: STM-64 + alias: OC-192 + description: SDH at 9.95 Gbit/s, still carried on plenty of transport networks. + layer: sdh + auto_selectable: true + bit_rate_kbps: 9953280 + default_container_type: ODU2 + default_mapping: BMP + +--- +# What the line side can do. The transponder mode is what generates the carrier on +# this network; the 400ZR mode belongs to a pluggable and is here so a coherent +# QSFP-DD has a mode to declare. Reach and required OSNR are vendor figures, a +# starting point for a budget rather than a guarantee. +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnOpticalMode + data: + - name: wl5e-400g-dp16qam + description: Coherent 400G on the transponder line side, long haul. + mode_class: transponder + modulation: DP-16QAM + line_rate_gbps: 400 + baud_mbaud: 56000 + required_osnr_mdb: 25000 + cd_tolerance_fs_per_nm: 50000000 + nominal_reach_m: 1000000 + fec_type: SD-FEC + fec_latency_ns: 5000 + - name: 400zr-dp16qam + description: OIF 400ZR in a pluggable, for a metro or campus reach. + mode_class: pluggable + modulation: DP-16QAM + line_rate_gbps: 400 + baud_mbaud: 59840 + required_osnr_mdb: 26000 + cd_tolerance_fs_per_nm: 2400000 + nominal_reach_m: 120000 + fec_type: cFEC + fec_latency_ns: 100 + --- apiVersion: infrahub.app/v1 kind: Object @@ -75,6 +134,15 @@ spec: description: 400G QSFP-DD grey client optic, 500 m over parallel single mode fiber. form_factor: QSFP-DD tunable: false + # A coherent pluggable, so the catalogue carries something that names a mode. It + # is tunable, which is what lets it sit on the dense grid at all. + - part_number: QDD-400G-ZR + manufacturer: Cisco + description: 400ZR coherent QSFP-DD, tunable across the C band. + form_factor: QSFP-DD + tunable: true + supported_modes: + - 400zr-dp16qam --- apiVersion: infrahub.app/v1 @@ -597,6 +665,55 @@ spec: site: SJC1 oms_a2b: nyc1-sjc1-oms-north oms_sequence: 2 + # The return direction. `oms_b2a` is a separate relationship from `oms_a2b`, so + # which chain an amplifier is in is stored by the graph rather than by a flag, + # and oms_sequence counts along the direction that chain amplifies. + - name: sjc1-amp02 + status: active + role: core + vendor: Ciena + model: 6500 SLA + amplifier_type: edfa + stage: booster + gain_mdb: 17000 + noise_figure_mdb: 5000 + max_total_output_power_mdbm: 23000 + site: SJC1 + oms_b2a: nyc1-sjc1-oms-north + oms_sequence: 1 + - name: nyc1-amp02 + status: active + role: core + vendor: Ciena + model: 6500 SLA + amplifier_type: edfa + stage: preamp + gain_mdb: 21000 + noise_figure_mdb: 4500 + max_total_output_power_mdbm: 20000 + site: NYC1 + oms_b2a: nyc1-sjc1-oms-north + oms_sequence: 2 + +--- +# Distributed Raman on the long south span, injected at the SJC1 end and firing +# back against the signal, which is what counter-propagating means. It pumps the +# fiber itself rather than sitting in line, so it points at the span. +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnRamanPump + data: + - name: sjc1-raman01 + status: active + role: core + vendor: Ciena + model: 6500 SLA + on_off_gain_mdb: 9000 + injection_end: site_b + propagation: counter + site: SJC1 + span: nyc1-sjc1-span-2 --- apiVersion: infrahub.app/v1 @@ -620,6 +737,12 @@ spec: kind: OtnFrequencyGrid data: center_frequency_mhz: 192100000 + optical_mode: wl5e-400g-dp16qam + # Both sections, because the working and the protect path each cross one and + # the carrier is the same wavelength on either route. + sections: + - nyc1-sjc1-oms-north + - nyc1-sjc1-oms-south line_ports: - [nyc1-txp01, line-1/0/0] - [sjc1-txp01, line-1/0/0] @@ -631,11 +754,12 @@ spec: kind: OtnService data: - name: SVC-NYC1-SJC1-0001 - description: 400G wavelength sold to Lumen between NYC1 and SJC1. + description: 400G transport sold to Lumen between NYC1 and SJC1, groomed into ODU containers. rate_gbps: 400 sla: gold status: active - service_type: wavelength + service_type: transport + client_signal: 100GBASE-LR4 max_latency_ns: 500000 endpoint_a: nyc1-txp01 endpoint_z: sjc1-txp01 @@ -759,3 +883,76 @@ spec: data: name: add-drop-1/1 device: nyc1-rdm01 + +--- +# The ODU hierarchy riding the 400G wavelength. The ODUC4 is the whole line +# payload; it offers 320 tributary slots, four ODU4 worth. One ODU4 carries a +# 100GbE client directly by GMP. The other multiplexes a lower-order ODU2, which +# carries an STM-64 by BMP, so the parent and child edges are exercised two levels +# deep. Slot counts are the G.709 figures: an ODU4 takes 80 slots in an ODUC4 and +# offers 80 to its own children; an ODU2 takes 8 of those. +# +# The parent is first in the list on purpose, since each child names it. +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnContainer + data: + - name: oduc4-nyc1-sjc1 + description: Line payload of the 400G wavelength. + odu_type: ODUC4 + mapping_mode: GMP + segment_sequence: 1 + tributary_slots: 0 + tributary_slot_capacity: 320 + carrier: nyc1-sjc1-ch16 + service: SVC-NYC1-SJC1-0001 + - name: odu4-nyc1-sjc1-1 + description: Carries the 100GbE client directly. + odu_type: ODU4 + mapping_mode: GMP + segment_sequence: 1 + tributary_slots: 80 + tributary_slot_capacity: 0 + parent_container: oduc4-nyc1-sjc1 + client_signal: 100GBASE-LR4 + service: SVC-NYC1-SJC1-0001 + - name: odu4-nyc1-sjc1-2 + description: Multiplexes lower-order containers rather than carrying a client. + odu_type: ODU4 + mapping_mode: GMP + segment_sequence: 1 + tributary_slots: 80 + tributary_slot_capacity: 80 + parent_container: oduc4-nyc1-sjc1 + service: SVC-NYC1-SJC1-0001 + - name: odu2-nyc1-sjc1-1 + description: Carries the STM-64 client, bit-synchronously mapped. + odu_type: ODU2 + mapping_mode: BMP + segment_sequence: 1 + tributary_slots: 8 + tributary_slot_capacity: 0 + parent_container: odu4-nyc1-sjc1-2 + client_signal: STM-64 + service: SVC-NYC1-SJC1-0001 + +--- +# An ODU cross-connect at the far end, which demultiplexes the line payload and +# regroups containers rather than passing the whole thing through. That is the +# difference `switching_mode` records. +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: OtnOduSwitch + data: + - name: sjc1-odusw01 + status: active + role: core + vendor: Ciena + model: 6500-T12 + switching_mode: cross_connect + framing_latency_ns: 12000 + site: SJC1 + carriers: + - nyc1-sjc1-ch16