diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000000000000000000000000000000000000..a6b9e8a385f1357cf14bb0d256cdef2285f17d65
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..c3317e56bc68664fb5404a1d76950381abaae63c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+/target/
+/.apt_generated/
+/.apt_generated_tests/
+/.factorypath
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e5ec948a16a5a84e0d5bfc8755819142a7f7fc4c
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,41 @@
+include:
+ - project: osl/code/org.etsi.osl.main
+ ref: main
+ file:
+ - ci-templates/default.yml
+ - ci-templates/build.yml
+ rules:
+ - if: '$CI_COMMIT_REF_NAME == "main"'
+
+ - project: osl/code/org.etsi.osl.main
+ ref: develop
+ file:
+ - ci-templates/default.yml
+ - ci-templates/build.yml
+ rules:
+ - if: '$CI_COMMIT_REF_NAME == "develop"'
+
+ - project: osl/code/org.etsi.osl.main
+ ref: $CI_COMMIT_REF_NAME
+ file:
+ - ci-templates/default.yml
+ - ci-templates/build.yml
+ rules:
+ - if: '$CI_COMMIT_REF_PROTECTED == "true" && $CI_COMMIT_REF_NAME != "main" && $CI_COMMIT_REF_NAME != "develop"'
+
+ - project: osl/code/org.etsi.osl.main
+ ref: develop
+ file:
+ - ci-templates/default.yml
+ - ci-templates/build_unprotected.yml
+ rules:
+ - if: '$CI_COMMIT_REF_NAME != "main" && $CI_COMMIT_REF_NAME != "develop" && $CI_COMMIT_REF_PROTECTED == "false"'
+
+maven_build:
+ extends: .maven_build
+
+docker_build:
+ extends: .docker_build
+ needs:
+ - maven_build
+
diff --git a/.project b/.project
new file mode 100644
index 0000000000000000000000000000000000000000..b2664e6cb7f86943faa24af18d052af38eb44503
--- /dev/null
+++ b/.project
@@ -0,0 +1,23 @@
+
+
+ org.etsi.osl.controllers.ietf.ns
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..29abf999564110a0d6aca109f55f439c72b7031c
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,6 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/main/resources=UTF-8
+encoding//src/test/java=UTF-8
+encoding//src/test/resources=UTF-8
+encoding/=UTF-8
diff --git a/.settings/org.eclipse.jdt.apt.core.prefs b/.settings/org.eclipse.jdt.apt.core.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..fa6bcfb3fdb3a5ff5ccf658c79e656a02d8dbf61
--- /dev/null
+++ b/.settings/org.eclipse.jdt.apt.core.prefs
@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.apt.aptEnabled=true
+org.eclipse.jdt.apt.genSrcDir=.apt_generated
+org.eclipse.jdt.apt.genTestSrcDir=.apt_generated_tests
+org.eclipse.jdt.apt.reconcileEnabled=true
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..d9cfd0599953a44fe774453d003a50ed89e46679
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,17 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=17
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.processAnnotations=enabled
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=17
diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..f897a7f1cb2389f85fe6381425d29f0a9866fb65
--- /dev/null
+++ b/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..a975b494285cf97d76a9ae91a32722ef86049c1e
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,27 @@
+# Multi-stage build for TFS Controller
+# Stage 1: Build the application
+# FROM maven:3.9.6-eclipse-temurin-17 AS builder
+
+# WORKDIR /build
+
+# # Copy pom.xml and download dependencies
+# COPY pom.xml .
+# RUN mvn dependency:go-offline -B
+
+# # Copy source code
+# COPY src ./src
+
+# # Build the application
+# RUN mvn clean package -DskipTests
+
+# Stage 2: Runtime image
+FROM eclipse-temurin:17-jdk-alpine
+
+WORKDIR /app
+
+# Copy the built JAR from builder stage
+# COPY --from=builder /build/target/org.etsi.osl.controllers.ietf.ns-0.0.9-SNAPSHOT.jar app.jar
+COPY target/org.etsi.osl.controllers.ietf.ns-0.0.9-SNAPSHOT-exec.jar app.jar
+
+# Run the application
+ENTRYPOINT ["java", "-jar", "app.jar"]
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
index 956fc3ddb9ebb54b3c1d8f64a8aefd31990a5ebe..b96d407d2d96672dca7a2a21901bf4e7b25d4fd3 100644
--- a/README.md
+++ b/README.md
@@ -1,93 +1,446 @@
-# org.etsi.osl.controllers.ietf.ns
+# IETF NS Controller - OpenSlice IETF RFC 9543 Network Slice Service Resource Controller for Teraflow SDN
+## Overview
+The **IETF NS Controller** is a Spring Boot microservice implementing the customer part of the **IETF RFC 9543 Network Slice Service** for controllers who support the IETF RFC 9543 like Teraflow SDN. This microservice acts as an **OSL Resource Controller** that bridges
+OpenSlice microservices (like TMF API, Service Orchestrator (OSOM)) and Network Slice Service Providers (e.g., TeraflowSDN) through a **message-driven architecture** using Apache Camel and ActiveMQ.
-## Getting started
+### Architectural approach
-To make it easy for you to get started with GitLab, here's a list of recommended next steps.
+```
+┌─────────────────────────────────────────────────────────────┐
+│ OpenSlice TMF API, Service Orchestrator (OSOM) │
+│ (Network Slice Service Customer per RFC 9543) │
+└──────────────────────┬──────────────────────────────────────┘
+ │
+ ActiveMQ/JMS Message Queues (CREATE/UPDATE/DELETE)
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ IETF NS Controller (This Service) │
+│ - Resource Controller for Network Slice Services │
+│ - Listens on JMS queues for resource operations │
+│ - Transforms between TMF and RFC 9543 models │
+│ - Manages resource specifications & instances │
+└──────────────────────┬──────────────────────────────────────┘
+ │
+ RESTCONF Protocol (RFC 8040 / RFC 9543)
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Network Slice Service Provider (e.g. TeraflowSDN) │
+│ (RESTCONF Server implementing RFC 9543) │
+└─────────────────────────────────────────────────────────────┘
+```
+
+## Key Responsibilities
+
+1. **Resource Controller Registration** - Automatically registers three fixed resource specifications under category `ns.ietf.controllers.osl.etsi.org/v1alpha` in the OpenSlice TMF catalog on startup
+2. **Message-Driven Operations** - Listens on ActiveMQ queues for CREATE/UPDATE/DELETE requests
+3. **Spec-Based Dispatch** - Routes incoming resource operations to different handlers based on the resource spec name (`IETFSloSleTemplateSpec`, `IETFSliceServiceSpec`, `IETFNetworkSliceServicesSpec`)
+4. **Model Transformation** - Converts between IETF RFC 9543 Network Slice models and TMF resource specifications using MapStruct and custom Jackson serializers/deserializers
+5. **RESTCONF Provisioning** - Provisions network slices to RFC 9543-compliant Network Slice Service Providers via RFC 8040 RESTCONF
+6. **Catalog Management** - Manages resource specifications and instances in the OpenSlice TMF catalog
+
+## Resource Specification Types
+
+Three fixed `LogicalResourceSpecification` entries are registered in the TMF catalog at startup, each under category `ns.ietf.controllers.osl.etsi.org/v1alpha`:
+
+| Spec Name | Constant | Description |
+|-----------|----------|-------------|
+| `IETFSloSleTemplateSpec` | `SPEC_SLO_SLE_TEMPLATE` | Defines the structure of an RFC 9543 SLO/SLE template (service level objectives and expectations) |
+| `IETFSliceServiceSpec` | `SPEC_SLICE_SERVICE` | Defines the structure of a single RFC 9543 Network Slice Service including SDPs, connection groups and SLO/SLE reference |
+| `IETFNetworkSliceServicesSpec` | `SPEC_NETWORK_SLICE_SERVICES` | Top-level container carrying SLO/SLE templates and slice services as JSON arrays |
+
+### Characteristics per Spec
+
+**IETFSloSleTemplateSpec** characteristics (derived from `SloSleTemplate` fields via MapStruct):
+- Per-field characteristics from `SloPolicy` (availability, MTU, metric bounds) and `SlePolicy` (security, isolation, path constraints)
+- `SloSleTemplateAsJson` (TEXT, configurable) — full RFC 9543 SLO/SLE template JSON blob
+
+**IETFSliceServiceSpec** characteristics:
+- `id`, `description`, `testOnly`
+- `status.adminStatus`, `status.operStatus`
+- `sloSleTemplate` (TEXT, stores the referenced template ID)
+- `serviceTags`, `sdps`, `connectionGroups` (ARRAY)
+- `SliceServiceAsJson` (TEXT, configurable) — full RFC 9543 slice service JSON blob
+
+**IETFNetworkSliceServicesSpec** characteristics:
+- `SloSleTemplatesAsJsonArray` (TEXT) — JSON array of all `slo-sle-template` entries
+- `SliceServicesAsJsonArray` (TEXT) — JSON array of all `slice-service` entries
+
+The UUID of each registered spec is stored in `ResourceSpecificationTemplateRegistry` and used when dispatching incoming resource operations.
+
+## Message-Driven Architecture
+
+### ActiveMQ Queue Pattern
+
+The controller listens for messages on queues following this pattern:
+
+```
+{CREATE|UPDATE|DELETE}/{category}/{version}
+```
+
+For this controller:
+- **Category:** `ns.ietf.controllers.osl.etsi.org/v1alpha`
+- **Version:** `0.1.0`
+
+**Example queue names:**
+- `CREATE/ns.ietf.controllers.osl.etsi.org/v1alpha/0.1.0`
+- `UPDATE/ns.ietf.controllers.osl.etsi.org/v1alpha/0.1.0`
+- `DELETE/ns.ietf.controllers.osl.etsi.org/v1alpha/0.1.0`
+
+### Message Flow
+
+**CREATE:**
+1. **OpenSlice OSOM** sends a resource request to the controller's CREATE queue
+2. **Apache Camel route** (`PartnerRouteBuilder`) intercepts the message and triggers `ResourceRepoService`
+3. **ResourceRepoService** reads the spec name from the resource's `resourceSpecification.name`
+4. **Spec-based dispatch:** routes to `applySloSleTemplateRequest`, `applySliceServiceRequest`, or `applyNetworkSliceServicesRequest` depending on the spec name
+5. **RFC 9543 deserializer** parses the JSON characteristic payload (`Rfc9543SloSleTemplateDeserializer` or `Rfc9543SliceServiceDeserializer`)
+6. **RESTCONF client** (`RestconfClientImpl`) provisions the resource to the Network Slice Service Provider
+7. **Response** is sent back via catalog update (resource status set to `AVAILABLE`, `TERMINATED`, or `UNKNOWN`)
+
+**DELETE:**
+1. **OpenSlice OSOM** sends a resource request to the controller's DELETE queue
+2. **Apache Camel route** triggers `ResourceRepoService.deleteResource()`
+3. **Spec detection from characteristics:** since `ResourceUpdate` carries no spec name, the handler inspects which characteristics are present in the resource:
+ - `SloSleTemplateAsJson` present → parses template ID → `RestconfConsumerService.decommissionSloSleTemplate(id)`
+ - `SliceServiceAsJson` present → parses service ID → `RestconfConsumerService.decommissionSliceService(id)`
+ - `SloSleTemplatesAsJsonArray` / `SliceServicesAsJsonArray` present → `RestconfConsumerService.decommissionNetworkSliceServices()`
+4. **Response:** status set to `TERMINATED` + Healthy on success, `UNKNOWN` + Unhealthy on RESTCONF failure, `TERMINATED` + Degraded when no RESTCONF consumer is configured
+
+### Message Headers
-Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
+Important metadata is included in message headers:
-## Add your files
+| Header | Description |
+|--------|-------------|
+| `org.etsi.osl.resourceId` | UUID of the resource created in OpenSlice (must be updated with status) |
+| `org.etsi.osl.serviceOrderId` | UUID of the related service order |
+| `org.etsi.osl.serviceId` | UUID of the related service |
-* [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
-* [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command:
+## Bootstrap Phase
+On startup, `SloSleTemplateBootstrapService` (implements `CommandLineRunner`) executes a 3-step sequence:
+
+**Step 1 — Create fixed resource specification templates**
+
+Three specifications are built in memory:
+- `createSloSleSpecification()` → `IETFSloSleTemplateSpec` + `SloSleTemplateAsJson` characteristic
+- `createSliceServiceSpecification()` → `IETFSliceServiceSpec` + `SliceServiceAsJson` characteristic
+- `createNetworkSliceServicesSpecification()` → `IETFNetworkSliceServicesSpec` + `SloSleTemplatesAsJsonArray` + `SliceServicesAsJsonArray` characteristics
+
+**Step 2 — Retrieve dynamic templates from RESTCONF Provider (optional)**
+
+If a `RestconfClient` bean is configured, the bootstrap calls:
+```
+GET /restconf/data/ietf-network-slice-service:network-slice-services/slo-sle-templates
+Accept: application/yang-data+json
+```
+The JSON response is parsed with `Rfc9543JsonConverter.parseSloSleTemplates()` and each resulting `SloSleTemplate` is converted to an additional `LogicalResourceSpecification` via `EntityToLogicalResourceSpecMapper`.
+
+If no RESTCONF client is available, this step is skipped with a warning.
+
+**Step 3 — Register all specifications in the TMF catalog**
+
+Each specification (fixed + any retrieved from provider) is posted to the TMF Catalog API:
+```
+POST /resourceCatalogManagement/v4/resourceSpecification
+```
+The returned UUID is stored in `ResourceSpecificationTemplateRegistry` under the spec name key.
+
+**Bootstrap sequence diagram:** [`doc/bootstrap_phase.puml`](doc/bootstrap_phase.puml)
+
+## RFC 9543 JSON Wire Format
+
+### Namespace Wrapper
+
+All RESTCONF payloads sent to the Network Slice Service Provider use the RFC 9543 namespace-qualified root:
+
+```json
+{
+ "ietf-network-slice-service:network-slice-services": {
+ "slo-sle-templates": {
+ "slo-sle-template": [ { "id": "gold", ... } ]
+ },
+ "slice-service": [
+ { "id": "service-001", ... }
+ ]
+ }
+}
```
-cd existing_repo
-git remote add origin https://labs.etsi.org/rep/osl/code/addons/org.etsi.osl.controllers.ietf.ns.git
-git branch -M main
-git push -uf origin main
+
+This wrapper is built in `RestconfClientImpl.buildRfc9543Payload(NetworkSliceServices)` using Jackson `ObjectNode`/`ArrayNode` directly, independent of domain model serialization.
+
+### SloSleTemplate Wire Format
+
+`SloSleTemplate` objects are serialized/deserialized with a matched pair of custom Jackson handlers annotated directly on the class:
+
+- **`Rfc9543SloSleTemplateSerializer`** (`StdSerializer`) — writes only the known RFC 9543 top-level fields (`id`, `description`, `slo-policy`, `sle-policy`, `template-ref`), preventing interface default methods from `LogicalResourceSpecMappable` / `RelatedManagedResourceReference` from leaking into the output. Nested `SloPolicy` and `SlePolicy` are delegated to Jackson's default serialization, which honours their `@JsonProperty` annotations.
+
+- **`Rfc9543SloSleTemplateDeserializer`** (`StdDeserializer`) — reads `"slo-policy"` and `"sle-policy"` keys explicitly and delegates nested object parsing to Jackson via `codec.treeToValue()`, so `SloPolicy` gets its `"metric-bound"` mapping, `SlePolicy` gets its `"max-occupancy-level"` and `"isolation-requirement"` mappings, etc.
+
+### SliceService Wire Format
+
+`SliceService` objects are serialized/deserialized with a matched pair of custom Jackson handlers:
+
+- **`Rfc9543SliceServiceSerializer`** (`StdSerializer`) — annotated on `SliceService` via `@JsonSerialize`. Produces RFC 9543 nested structure:
+ - `"sdps": { "sdp": [...] }`
+ - `"connection-groups": { "connection-group": [...] }`
+ - `"service-tags": { "tag-type": [...] }`
+ - `"slo-sle-template": ""` (plain string reference, not object)
+ - `"status": { "admin-status": "...", "oper-status": "..." }`
+
+- **`Rfc9543SliceServiceDeserializer`** (`StdDeserializer`) — annotated on `SliceService` via `@JsonDeserialize`. Handles:
+ - Direct `"slo-sle-template": "silver"` string reference at service level
+ - Nested `"slo-sle-policy": { "slo-sle-template": "..." }` variant
+ - `"sdps"` as `{"sdp": [...]}` or flat array
+ - `"connection-groups"` as `{"connection-group": [...]}` or flat array
+ - `"service-tags": { "tag-type": [{ "tag-type": "...", "tag-type-value": [...] }] }`
+
+### Field Name Mapping
+
+All RFC 9543 hyphenated JSON fields are mapped to Java camelCase using `@JsonProperty`:
+
+| RFC 9543 JSON field | Java property | Class |
+|---------------------|---------------|-------|
+| `slo-sle-template` | `sloSleTemplate` | `SliceService` (via deserializer) |
+| `connection-groups` | `connectionGroups` | `SliceService` (via deserializer) |
+| `sdp-ip-address` | `sdpIpAddress` | `SDP` |
+| `p2p-sender-sdp` | `p2pSenderSdp` | `ConnectivityConstruct` |
+| `p2p-receiver-sdp` | `p2pReceiverSdp` | `ConnectivityConstruct` |
+| `slo-policy` | `sloPolicy` | `SloSleTemplate` |
+| `sle-policy` | `slePolicy` | `SloSleTemplate` |
+| `metric-bound` | `metricBounds` | `SloPolicy` |
+| `metric-type` | `metricType` | `MetricBound` |
+| `metric-unit` | `metricUnit` | `MetricBound` |
+| `value-description` | `valueDescription` | `MetricBound` |
+| `percentile-value` | `percentileValue` | `MetricBound` |
+| `max-occupancy-level` | `maxOccupancyLevel` | `SlePolicy` |
+| `isolation-requirement` | `isolation` | `SlePolicy` |
+| `path-constraints` | `pathConstraints` | `SlePolicy` |
+| `admin-status` | `adminStatus` | `ServiceStatus` |
+| `oper-status` | `operStatus` | `ServiceStatus` |
+
+### Enum Handling
+
+All RFC 9543 enums use `@JsonCreator` / `@JsonValue` for automatic kebab-case ↔ UPPER_SNAKE_CASE conversion:
+
+```java
+// Deserialization: "two-way-bandwidth" → TWO_WAY_BANDWIDTH
+@JsonCreator
+public static ServiceSloMetricType fromValue(String value) {
+ String normalized = value.toUpperCase().replace('-', '_');
+ return ServiceSloMetricType.valueOf(normalized); // returns null on blank/unknown
+}
+
+// Serialization: TWO_WAY_BANDWIDTH → "two-way-bandwidth"
+@JsonValue
+public String toValue() { return name().toLowerCase().replace('_', '-'); }
```
-## Integrate with your tools
+Enums with this pattern: `ServiceSloMetricType`, `ServiceSecurityType`, `ServiceIsolationType`, `ConnectivityType`.
+
+The Jackson `ObjectMapper` used for parsing characteristics is configured with:
+```java
+ObjectMapper mapper = new ObjectMapper()
+ .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
+ .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
+mapper.setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP));
+```
+This tolerates unknown fields, single values where arrays are expected, and empty-string enum values in collection fields.
-* [Set up project integrations](https://labs.etsi.org/rep/osl/code/addons/org.etsi.osl.controllers.ietf.ns/-/settings/integrations)
+## Project Structure
-## Collaborate with your team
+```
+src/main/java/org/etsi/osl/controllers/ietf/ns/
+├── IETFNSGCSpringBoot.java (Application entry point)
+├── api/
+│ ├── SloSleTemplateBootstrapService.java (3-step bootstrap on startup)
+│ ├── ResourceRepoService.java (Message handler & spec-based dispatch)
+│ ├── ResourceSpecificationTemplateRegistry.java (Stores spec name → UUID mappings)
+│ ├── CategoryConfigurationService.java (Category/version config)
+│ ├── PartnerRouteBuilder.java (Camel EIP routes)
+│ ├── config/
+│ │ ├── CatalogClient.java (TMF Catalog API integration)
+│ │ ├── ActiveMQComponentConfig.java (Message broker setup)
+│ │ └── RestconfConfig.java (RESTCONF client configuration)
+│ ├── restconf/
+│ │ ├── RestconfClient.java (RESTCONF interface: CRUD + feasibility)
+│ │ ├── RestconfClientImpl.java (HTTP/HTTPS via Spring RestTemplate)
+│ │ ├── RestconfConsumerService.java (High-level business logic)
+│ │ ├── Rfc9543JsonConverter.java (Parses RFC 9543 RESTCONF responses)
+│ │ └── RestconfException.java (Structured YANG error handling)
+│ └── domain/model/
+│ ├── NetworkSliceServices.java (Top-level RFC 9543 container)
+│ ├── SliceService.java (@JsonSerialize/@JsonDeserialize pair)
+│ ├── SloSleTemplate.java (@JsonSerialize/@JsonDeserialize pair)
+│ ├── SDP.java (Service Demarcation Point)
+│ ├── ConnectionGroup.java (Connectivity group)
+│ ├── ConnectivityConstruct.java (P2P/P2MP/A2A endpoints)
+│ ├── ServiceStatus.java (admin-status / oper-status)
+│ ├── ServiceTag.java (service-tags entry)
+│ ├── ConnectivityType.java (Enum: P2P, P2MP, A2A)
+│ ├── Rfc9543SloSleTemplateSerializer.java (StdSerializer for SloSleTemplate)
+│ ├── Rfc9543SloSleTemplateDeserializer.java (StdDeserializer for SloSleTemplate)
+│ ├── Rfc9543SliceServiceSerializer.java (StdSerializer for SliceService)
+│ ├── Rfc9543SliceServiceDeserializer.java (StdDeserializer for SliceService)
+│ └── slo_sle/
+│ ├── SloPolicy.java (Service Level Objectives)
+│ ├── SlePolicy.java (Service Level Expectations)
+│ ├── MetricBound.java (Performance metric constraint)
+│ ├── AvailabilityType.java (Availability percentage + periods)
+│ ├── PathConstraints.java (Path diversity/disjointness)
+│ ├── ServiceSloMetricType.java (14 metric type enum, @JsonCreator/@JsonValue)
+│ ├── ServiceSecurityType.java (Security requirement enum)
+│ └── ServiceIsolationType.java (Isolation requirement enum)
+├── domain/common/
+│ ├── LogicalResourceMappable.java (Interface for TMF LogicalResource mapping)
+│ ├── LogicalResourceSpecMappable.java (Interface for TMF Spec mapping)
+│ ├── RelatedManagedResourceReference.java (Interface for resource relationships)
+│ └── ExcludeFromMapping.java (Annotation to exclude from MapStruct)
+├── mappers/
+│ ├── EntityToLogicalResourceSpecMapper.java (MapStruct: domain → ResourceSpec)
+│ └── LogicalResourceToEntityMapper.java (MapStruct: ResourceSpec/Resource → domain)
+└── repository/impl/
+ ├── TMFResourceSpecRepositoryImpl.java (TMF Catalog API client)
+ └── TMFResourceInventoryRepositoryImpl.java (TMF Inventory API client)
+
+doc/
+├── bootstrap_phase.puml (Bootstrap sequence diagram)
+├── resource_creation_lifecycle.puml (Resource lifecycle diagram)
+└── NSC/
+ ├── RESTCONF_CONSUMER_GUIDE.md
+ ├── RESTCONF_IMPLEMENTATION.md
+ ├── SLO_SLE_TEMPLATE_GUIDE.md
+ ├── SLO_SLE_REFACTORING_SUMMARY.md
+ ├── draft-ietf-teas-ietf-network-slice-nbi-yang-25.txt
+ └── examples/ (RFC 9543 JSON test payloads)
+```
-* [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
-* [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
-* [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
-* [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
-* [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/)
+## OpenSlice Integration
+
+### Resource Lifecycle
+
+When OpenSlice OSOM creates a resource using a registered resource specification:
+
+1. OSOM sends a `CREATE` message to `CREATE/ns.ietf.controllers.osl.etsi.org/v1alpha/0.1.0`
+2. Controller reads the spec name from the resource's `resourceSpecification.name`
+3. Handler parses the JSON characteristic payload and deserializes to domain objects
+4. Controller calls `RestconfClientImpl` to provision the slice via RESTCONF to TeraflowSDN
+5. Controller updates the resource status in OpenSlice:
+ - **CREATE success:** Status = `AVAILABLE`, administrative state = `UNLOCKED`
+ - **CREATE failure:** Status = `UNKNOWN`, health = `Unhealthy`, message describes error
+ - **DELETE success:** Status = `TERMINATED`, health = `Healthy`
+ - **DELETE failure:** Status = `UNKNOWN`, health = `Unhealthy`, message describes error
+
+**Resource creation sequence diagram:** [`doc/resource_creation_lifecycle.puml`](doc/resource_creation_lifecycle.puml)
+
+### jsonRequest and SliceService Model
+
+The `SliceServiceAsJson` characteristic carries the full RFC 9543 Network Slice Service JSON. The correct wire format uses RFC 9543 hyphenated names and nested containers:
+
+```json
+{
+ "id": "example-slice-001",
+ "description": "Example network slice with SLO/SLE template silver",
+ "slo-sle-template": "silver",
+ "service-tags": {
+ "tag-type": [
+ { "tag-type": "service", "tag-type-value": ["L3"] }
+ ]
+ },
+ "sdps": {
+ "sdp": [
+ { "id": "CU-N32", "node-id": "A", "sdp-ip-address": ["10.60.11.3"] },
+ { "id": "UPF-N32", "node-id": "B", "sdp-ip-address": ["10.60.10.6"] }
+ ]
+ },
+ "connection-groups": {
+ "connection-group": [
+ {
+ "id": "cg-001",
+ "connectivity-type": "point-to-point",
+ "connectivity-construct": [
+ { "id": "cc-001", "p2p-sender-sdp": "CU-N32", "p2p-receiver-sdp": "UPF-N32" }
+ ]
+ }
+ ]
+ }
+}
+```
-## Test and Deploy
+The `SloSleTemplatesAsJsonArray` characteristic on `IETFNetworkSliceServicesSpec` carries an array of SLO/SLE template objects in the same hyphenated RFC 9543 format.
-Use the built-in continuous integration in GitLab.
+## Getting Started
-* [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/)
-* [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
-* [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
-* [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
-* [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
+### Prerequisites
-***
+- Java 17+
+- Maven 3.6+
+- ActiveMQ/Artemis running on `tcp://localhost:61616` (default credentials: artemis/artemis)
+- OpenSlice OSOM with TMF Catalog API
+- RFC 9543 Network Slice Service Provider (RESTCONF endpoint, optional)
-# Editing this README
+### Build and Run
-When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
+```bash
+# Build the application
+mvn clean package
-## Suggestions for a good README
+# Run locally (auto-starts server)
+mvn spring-boot:run
-Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
+# Run packaged JAR
+java -jar target/org.etsi.osl.controllers.ietf.ns-0.0.9-SNAPSHOT.jar
-## Name
-Choose a self-explaining name for your project.
+# Build without tests
+mvn clean package -DskipTests
+```
-## Description
-Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
+### Docker Deployment
-## Badges
-On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
+```bash
+docker build -t ns-ietf-controller:latest .
-## Visuals
-Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
+docker run -p 8080:8080 \
+ -e SPRING_ACTIVEMQ_BROKERURL=tcp://activemq:61616 \
+ -e SPRING_APPLICATION_NAME=ns-ietf-controller \
+ ns-ietf-controller:latest
+```
-## Installation
-Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
+## Configuration
-## Usage
-Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
+All runtime settings are configured in `application.yml`:
-## Support
-Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
+- **Server port:** 0 (OS assigns available port — check startup logs)
+- **ActiveMQ broker URL:** `tcp://localhost:61616`
+- **Queue names:** 21 configured endpoints (see `application.yml`)
+- **RESTCONF provider URL:** Configure in `RestconfConfig` (if omitted, Step 2 of bootstrap is skipped)
+- **Category:** `ns.ietf.controllers.osl.etsi.org/v1alpha`
+- **OAuth signing key:** For resource update signatures
-## Roadmap
-If you have ideas for releases in the future, it is a good idea to list them in the README.
+## Technology Stack
-## Contributing
-State if you are open to contributions and what your requirements are for accepting them.
+- **Framework:** Spring Boot 3.2.2, Java 17
+- **Messaging:** Apache Camel 4.0.0-RC2, Apache ActiveMQ (JMS)
+- **RESTCONF:** Custom implementation per RFC 8040, targeting RFC 9543 data model
+- **Object Mapping:** MapStruct 1.5.3.Final (requires annotation processing)
+- **JSON Processing:** Jackson with `@JsonProperty`, `@JsonCreator`/`@JsonValue` on enums, and custom `StdSerializer`/`StdDeserializer` for `SliceService`
+- **Security:** Spring Security, OAuth2 resource server, Keycloak 22.0.1
+- **Utilities:** Lombok 1.18.28, Guava 32.0.0
-For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
+## Documentation
-You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
+**Architecture Diagrams:**
+- [`doc/bootstrap_phase.puml`](doc/bootstrap_phase.puml) — 3-step bootstrap sequence
+- [`doc/resource_creation_lifecycle.puml`](doc/resource_creation_lifecycle.puml) — resource lifecycle from OSOM to RESTCONF
-## Authors and acknowledgment
-Show your appreciation to those who have contributed to the project.
+**Test Resources:**
+- `doc/NSC/examples/` — RFC 9543 JSON test payloads
+- `doc/NSC/draft-ietf-teas-ietf-network-slice-nbi-yang-25.txt` — RFC 9543 specification
## License
-For open source projects, say how it is licensed.
-## Project status
-If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
+This project is part of the OpenSlice OSL ETSI SDG, licensed under Apache 2.0
diff --git a/ci_settings.xml b/ci_settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..eade2387d3c1f28dad1f4b49c2e14a94ff715ac2
--- /dev/null
+++ b/ci_settings.xml
@@ -0,0 +1,17 @@
+
+
+
+ gitlab-maven
+
+
+
+ Job-Token
+ ${CI_JOB_TOKEN}
+
+
+
+
+
+
+
diff --git a/compose.yml b/compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5df64520d89ae1243d7ed877cd26695eb541a34f
--- /dev/null
+++ b/compose.yml
@@ -0,0 +1,31 @@
+services:
+ osl-ietf-ns-controller:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ image: labs.etsi.org:5050/osl/code/addons/org.etsi.osl.controllers.ietf.ns:develop
+ container_name: osl-ietf-ns-controller
+ restart: always
+ profiles: ["dev", "prod"]
+ environment:
+ SPRING_ACTIVEMQ_BROKERURL: tcp://anartemis:61616?jms.watchTopicAdvisories=false
+ SPRING_ACTIVEMQ_USER: artemis
+ SPRING_ACTIVEMQ_PASSWORD: artemis
+ RESTCONF_PROVIDERURL: "http://nscontroller:8085"
+ RESTCONF_AUTHMETHOD: "basic"
+ RESTCONF_AUTH_USERNAME: "admin"
+ RESTCONF_AUTH_PASSWORD: "admin"
+ RESTCONF_APIVERSION: "2025-05-09"
+ LOGGING_LEVEL_ORG_SPRINGFRAMEWORK: INFO
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "250m"
+ max-file: "2"
+ networks:
+ - compose_back
+
+networks:
+ compose_back:
+ external: true
+
\ No newline at end of file
diff --git a/doc/NSC/draft-ietf-teas-ietf-network-slice-nbi-yang-25.txt b/doc/NSC/draft-ietf-teas-ietf-network-slice-nbi-yang-25.txt
new file mode 100644
index 0000000000000000000000000000000000000000..dc78cd4583d7355bfa1dcf8081db83949f589798
--- /dev/null
+++ b/doc/NSC/draft-ietf-teas-ietf-network-slice-nbi-yang-25.txt
@@ -0,0 +1,7056 @@
+
+
+
+
+TEAS B. Wu
+Internet-Draft D. Dhody
+Intended status: Standards Track Huawei Technologies
+Expires: 10 November 2025 R. Rokui
+ Ciena
+ T. Saad
+ J. Mullooly
+ Cisco Systems, Inc
+ 9 May 2025
+
+
+ A YANG Data Model for the RFC 9543 Network Slice Service
+ draft-ietf-teas-ietf-network-slice-nbi-yang-25
+
+Abstract
+
+ This document defines a YANG data model for RFC 9543 Network Slice
+ Service. The model can be used in the Network Slice Service
+ interface between a customer and a provider that offers RFC 9543
+ Network Slice Services.
+
+Status of This Memo
+
+ This Internet-Draft is submitted in full conformance with the
+ provisions of BCP 78 and BCP 79.
+
+ Internet-Drafts are working documents of the Internet Engineering
+ Task Force (IETF). Note that other groups may also distribute
+ working documents as Internet-Drafts. The list of current Internet-
+ Drafts is at https://datatracker.ietf.org/drafts/current/.
+
+ Internet-Drafts are draft documents valid for a maximum of six months
+ and may be updated, replaced, or obsoleted by other documents at any
+ time. It is inappropriate to use Internet-Drafts as reference
+ material or to cite them other than as "work in progress."
+
+ This Internet-Draft will expire on 10 November 2025.
+
+Copyright Notice
+
+ Copyright (c) 2025 IETF Trust and the persons identified as the
+ document authors. All rights reserved.
+
+ This document is subject to BCP 78 and the IETF Trust's Legal
+ Provisions Relating to IETF Documents (https://trustee.ietf.org/
+ license-info) in effect on the date of publication of this document.
+ Please review these documents carefully, as they describe your rights
+ and restrictions with respect to this document. Code Components
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 1]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ extracted from this document must include Revised BSD License text as
+ described in Section 4.e of the Trust Legal Provisions and are
+ provided without warranty as described in the Revised BSD License.
+
+Table of Contents
+
+ 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
+ 2. Conventions and Definitions . . . . . . . . . . . . . . . . . 4
+ 2.1. Acronyms . . . . . . . . . . . . . . . . . . . . . . . . 5
+ 3. Network Slice Service Overview . . . . . . . . . . . . . . . 5
+ 4. Network Slice Service Model (NSSM) Usage . . . . . . . . . . 7
+ 5. Network Slice Service Model (NSSM) Description . . . . . . . 8
+ 5.1. SLO and SLE Templates . . . . . . . . . . . . . . . . . . 10
+ 5.2. Network Slice Services . . . . . . . . . . . . . . . . . 12
+ 5.2.1. Service Demarcation Points . . . . . . . . . . . . . 13
+ 5.2.2. Connectivity Constructs . . . . . . . . . . . . . . . 19
+ 5.2.3. SLO and SLE Policy . . . . . . . . . . . . . . . . . 21
+ 5.2.4. Network Slice Service Performance Monitoring . . . . 24
+ 5.2.5. Custom Topology Constraints . . . . . . . . . . . . . 25
+ 5.2.6. Network Slice Service Feasibility Check . . . . . . . 26
+ 6. Network Slice Service Module . . . . . . . . . . . . . . . . 27
+ 7. Security Considerations . . . . . . . . . . . . . . . . . . . 57
+ 8. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 59
+ 9. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 59
+ 10. Contributors . . . . . . . . . . . . . . . . . . . . . . . . 60
+ 11. References . . . . . . . . . . . . . . . . . . . . . . . . . 60
+ 11.1. Normative References . . . . . . . . . . . . . . . . . . 60
+ 11.2. Informative References . . . . . . . . . . . . . . . . . 63
+ 11.3. References . . . . . . . . . . . . . . . . . . . . . . . 64
+ Appendix A. Augmentation Considerations . . . . . . . . . . . . 64
+ Appendix B. Examples of Network Slice Services . . . . . . . . . 66
+ B.1. Example-1: Two A2A Slice Services with Different Match
+ Approaches . . . . . . . . . . . . . . . . . . . . . . . 66
+ B.2. Example-2: Two P2P Slice Services with Different Match
+ Approaches . . . . . . . . . . . . . . . . . . . . . . . 73
+ B.3. Example-3: A Hub and Spoke Slice Service with a P2MP
+ Connectivity Construct . . . . . . . . . . . . . . . . . 85
+ B.4. Example-4: An A2A Slice Service with Multiple SLOs and DSCP
+ Matching . . . . . . . . . . . . . . . . . . . . . . . . 92
+ B.5. Example-5: An A2A Network Slice Service with SLO Precedence
+ Policies . . . . . . . . . . . . . . . . . . . . . . . . 99
+ B.6. Example-6: SDP at CE, L3 A2A Slice Service . . . . . . . 106
+ B.7. Example-7: SDP at CE, L3 A2A Slice Service with Network
+ Abstraction . . . . . . . . . . . . . . . . . . . . . . . 112
+ Appendix C. Complete Model Tree Structure . . . . . . . . . . . 117
+ Appendix D. Comparison with the Design Choice of ACTN VN Model
+ Augmentation . . . . . . . . . . . . . . . . . . . . . . 125
+ Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 126
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 2]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+1. Introduction
+
+ [RFC9543] outlines a framework and an interface for Network Slice
+ using IETF technologies, and it introduces the term "IETF Network
+ Slice Service", referred to as RFC 9543 Network Slice Service. This
+ document uses the term "Network Slice Service" to refer to this
+ concept for brevity and consistency.
+
+ This document defines a YANG [RFC7950] data model for [RFC9543]
+ Network Slice Service. The Network Slice Service Model (NSSM) can be
+ used in the Network Slice Service Interface exposed by a provider to
+ its customers (including for provider's internal use) in order to
+ manage (e.g., subscribe, delete, or change) Network Slice Services.
+ The agreed service will then trigger the appropriate Network Slice
+ operation, such as instantiating, modifying, or deleting a Network
+ Slice.
+
+ The NSSM focuses on the requirements of a Network Slice Service from
+ the point of view of the customer, not how it is implemented within a
+ provider network. As discussed in [RFC9543], the mapping between a
+ Network Slice Service and its realization is implementation and
+ deployment specific.
+
+ The NSSM is classified as a customer service model (Section 2 of
+ [RFC8309]).
+
+ The NSSM conforms to the Network Management Datastore Architecture
+ (NMDA) [RFC8342].
+
+ Editorial Note: (To be removed by RFC Editor)
+
+ This document contains several placeholder values that need to be
+ replaced with finalized values at the time of publication. Please
+ apply the following replacements:
+
+ * AAAA --> the assigned RFC value for this draft both in this draft
+ and in the YANG models under the revision statement
+
+ * BBBB --> the assigned RFC value for
+ [I-D.ietf-opsawg-teas-common-ac]
+
+ * CCCC --> the assigned RFC value for
+ [I-D.ietf-opsawg-teas-attachment-circuit]
+
+ * DDDD --> the assigned RFC value for [I-D.ietf-teas-rfc8776-update]
+
+ * The "revision" date in model, in the format XXXX-XX-XX, needs to
+ be updated with the date the draft gets approved
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 3]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+2. Conventions and Definitions
+
+ The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
+ "OPTIONAL" in this document are to be interpreted as described in
+ BCP14, [RFC2119], [RFC8174] when, and only when, they appear in all
+ capitals, as shown here.
+
+ The following terms are defined in [RFC6241] and are used in this
+ specification:
+
+ * client
+
+ * configuration data
+
+ * state data
+
+ This document makes use of the terms defined in [RFC7950].
+
+ The tree diagrams used in this document follow the notation defined
+ in [RFC8340].
+
+ This document also makes use of the terms defined in [RFC9543]:
+
+ * Customer: See Section 3.2 of [RFC9543].
+
+ * Customer Higher-level Operation System: See Section 6.3.1 of
+ [RFC9543].
+
+ In addition, this document defines the following term:
+
+ * Connection Group: Refers to one or more Connectivity Constructs
+ that are grouped for administrative purposes, such as the
+ following:
+
+ - Combine multiple Connectivity Constructs to support a set of
+ well-known connectivity service types, such as bidirectional
+ unicast service, multipoint-to-point (MP2P) service, or hub-
+ and-spoke service.
+
+ - Assign the same Service Level Objectives (SLOs)/Service Level
+ Expectations (SLEs) policies to multiple Connectivity
+ Constructs unless the SLOs/SLEs policy is explicitly overridden
+ at the individual Connectivity Construct level.
+
+ - Share specific SLO limits within multiple Connectivity
+ Constructs.
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 4]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+2.1. Acronyms
+
+ The following acronyms are used in the document:
+
+ A2A Any-to-any
+ AC Attachment Circuit, as defined in Section 3.2 of [RFC9543]
+ CC Connectivity Construct: See Sections 3.2 and 4.2.1 of
+ [RFC9543]
+ CE Customer Edge, see Section 3.2 of [RFC9543]
+ MTU Maximum Transmission Unit
+ NSC Network Slice Controller, defined in Section 6.3 of [RFC9543]
+ NSS Network Slice Service, defined in Section 4.2 of [RFC9543]
+ NSSM Network Slice Service Model, defined in this document
+ P2P Point-to-point
+ P2MP Point-to-multipoint
+ PE Provider Edge, see Section 3.2 of [RFC9543]
+ QoS Quality of Service
+ SDP Service Demarcation Point, defined in Sections 3.2 and 5.2
+ [RFC9543]
+ SLE Service Level Expectation, defined in Section 5.1.2 of
+ [RFC9543]
+ SLO Service Level Objective, defined in Section 5.1.1 of
+ [RFC9543]
+
+
+3. Network Slice Service Overview
+
+ As defined in Section 3.2 of [RFC9543], a Network Slice Service is
+ specified in terms of a set of Service Demarcation Points (SDPs), a
+ set of one or more Connectivity Constructs between subsets of these
+ SDPs, and a set of Service Level Objectives (SLOs) and Service Level
+ Expectations (SLEs) for each SDP sending to each Connectivity
+ Construct. A communication type (point-to-point (P2P), point-to-
+ multipoint (P2MP), or any-to-any (A2A)) is specified for each
+ Connectivity Construct.
+
+ The SDPs serve as the Network Slice Service ingress/egress points.
+ An SDP is identified by a unique identifier in the context of a
+ Network Slice Service.
+
+ Examples of Network Slice Services that contain only one Connectivity
+ Construct are shown in Figure 1.
+
+
+
+
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 5]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ +----------------------------------------------+
+ | |
+ | |
+ | Slice Service 1 with 1 P2P CC |
+ SDP1 O------------------->--------------------------O SDP2
+ | |
+ | |
+ | Slice Service 2 with 1 P2MP CC
+ | +---------------------------O SDP4
+ SDP3 O----------->------+ |
+ | +---------------------------O SDP5
+ | |
+ | |
+ | Slice Service 3 with 1 A2A CC
+ SDP6 O-----------<>-----+---------<>----------------O SDP8
+ | | |
+ SDP7 O-----------<>-----+---------<>----------------O SDP9
+ | |
+ | |
+ +----------------------------------------------+
+ |<------------Network Slice Services---------->|
+ | between endpoints SDP1 to SDP9 |
+
+ CC: Connectivity Construct
+ O: Represents an SDP
+ ----: Represents Connectivity Construct
+ < > : Inbound/outbound directions
+
+ Figure 1: Examples of Network Slice Services of Single
+ Connectivity Construct
+
+ An example of Network Slice Services that contains multiple
+ Connectivity Constructs is shown in Figure 2.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 6]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ +----------------------------------------------+
+ | |
+ | Slice Service 4 with 2 P2P CCs |
+ SDP10 O------------------->--------------------------O SDP12
+ SDP11 O------------------->--------------------------O SDP13
+ | |
+ | |
+ | Slice Service 5 with 2 P2P CCs |
+ | +----------------->-----------------------+ |
+ SDP14 O/ \ O SDP15
+ |\ / |
+ | +-----------------<-----------------------+ |
+ | |
+ +----------------------------------------------+
+ |<-----------Network Slice Services----------->|
+ | between endpoints SDP10 to SDP15 |
+
+
+ Slice Service: Network Slice Service
+ CC: Connectivity Construct
+ O: Represents an SDP
+ ----: Represents Connectivity Construct
+ < > : Inbound/outbound directions
+
+ Figure 2: Examples of Network Slice Services of Multiple Connectivity
+ Constructs
+
+ As shown in Figure 2, the Network Slice Service 4 contains two P2P
+ Connectivity Constructs between the set of SDPs. The Network Slice
+ Service 5 is a bidirectional unicast service between SDP14 and SDP15
+ that consists of two unidirectional P2P Connectivity Constructs.
+
+4. Network Slice Service Model (NSSM) Usage
+
+ The NSSM can be used by a provider to expose its Network Slice
+ Services, and by a customer to manage its Network Slices Services
+ (e.g., request, delete, or modify). The details about how service
+ requests are handled by a provider (specifically, a controller),
+ including which network operations are triggered, are internal to the
+ provider. The details of the Network Slices realization are hidden
+ from customers.
+
+ The Network Slices are applicable to use cases, such as (but not
+ limited to) 5G, network wholesale services, network infrastructure
+ sharing among operators, Network Function Virtualization (NFV)
+ connectivity, and Data Center interconnect.
+ [I-D.ietf-teas-ietf-network-slice-use-cases] provides a more detailed
+ description of the use cases for Network Slices.
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 7]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ A Network Slice Controller (NSC) is an entity that exposes the
+ Network Slice Service Interface to customers to manage Network Slice
+ Services. Typically, an NSC receives requests from its customer-
+ facing interface (e.g., from a management system). During service
+ creation, this interface can convey data objects that the Network
+ Slice Service customer provides, describing the needed Network Slices
+ Service in terms of SDPs, the associated Connectivity Constructs, and
+ the service objectives that the customer wishes to be fulfilled.
+ Depending on whether the requirements and authorization checks are
+ successfully met, these service requirements are then translated into
+ technology-specific actions that are implemented in the underlying
+ network(s) using a network-facing interface. The details of how the
+ Network Slices are put into effect are out of scope for this
+ document.
+
+ As shown in Figure 3, the NSSM is used by the Customer Higher-level
+ Operation System to communicate with an NSC for life cycle management
+ of Network Slice Services including both enablement and monitoring.
+ For example, in the 5G End-to-end network slicing use case, the 5G
+ network slice orchestrator acts as the higher layer system to manage
+ the Network Slice Services. The interface is used to support Network
+ Slice management to facilitate end-to-end 5G network slice services.
+
+ +----------------------------------------+
+ | Network Slice Customer |
+ | (e.g., 5G network slice orchestrator)|
+ +----------------+-----------------------+
+ |
+ |
+ | Network Slice Service Model (NSSM)
+ |
+ +---------------------+--------------------------+
+ | Network Slice Controller (NSC) |
+ +------------------------------------------------+
+
+ Figure 3: Network Slice Service Reference Architecture
+
+ Note: The NSSM can be used recursively (hierarchical mode), i.e., an
+ NSS can map to child NSSes. As described in Section A.5 of
+ [RFC9543], the Network Slice Service can support a recursive
+ composite architecture that allows one layer of Network Slice
+ Services to be used by other layers.
+
+5. Network Slice Service Model (NSSM) Description
+
+ The NSSM, "ietf-network-slice-service", includes two main data nodes:
+ "slo-sle-templates" and "slice-service" ( Figure 4).
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 8]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ module: ietf-network-slice-service
+ +--rw network-slice-services
+ +--rw slo-sle-templates
+ | +--rw slo-sle-template* [id]
+ | ...
+ +--rw slice-service* [id]
+ +--rw id string
+ +--rw description? string
+ +--rw service-tags
+ | ...
+ +--rw (slo-sle-policy)?
+ | ...
+ +--rw test-only? empty
+ +--rw status
+ | ...
+ +--rw sdps
+ | ...
+ +--rw connection-groups
+ | ...
+ +--rw custom-topology
+ ...
+
+ Figure 4: The NSSM Overall Tree Structure
+
+ The "slo-sle-templates" container is used by an NSC to maintain a set
+ of common Network Slice SLO and SLE templates that apply to one or
+ several Network Slice Services. Refer to Section 5.1 for further
+ details on the properties of NSS templates.
+
+ The "slice-service" list includes the set of Network Slice Services
+ that are maintained by a provider for a given customer. "slice-
+ service" is the data structure that abstracts the Network Slice
+ Service. Under the "slice-service", the "sdp" list is used to
+ abstract the SDPs. The "connection-group" is used to abstract
+ Connectivity Constructs between SDPs. Refer to Section 5.2 for
+ further details on the properties of an NSS.
+
+ To ensure scalability of the Network Slice Service as the number of
+ slices increases, "slo-sle-templates" can be utilized to reuse
+ existing SLO/SLE policies. And the SDPs and connection constructs
+ can be incrementally updated to minimize the overhead associated with
+ frequent modifications.
+
+
+
+
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 9]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+5.1. SLO and SLE Templates
+
+ The "slo-sle-templates" container (Figure 5) is used by a Network
+ Slice Service provider to define and maintain a set of common Network
+ Slice Service templates that apply to one or several Network Slice
+ Services. The templates are assumed to be known to both the
+ customers and the provider. The exact definition of the templates is
+ deployment specific.
+
+ +--rw slo-sle-templates
+ | +--rw slo-sle-template* [id]
+ | +--rw id string
+ | +--rw description? string
+ | +--rw template-ref? slice-template-ref
+ | +--rw slo-policy
+ | | +--rw metric-bound* [metric-type]
+ | | | +--rw metric-type identityref
+ | | | +--rw metric-unit string
+ | | | +--rw value-description? string
+ | | | +--rw percentile-value? percentile
+ | | | +--rw bound? uint64
+ | | +--rw availability? identityref
+ | | +--rw mtu? uint32
+ | +--rw sle-policy
+ | +--rw security* identityref
+ | +--rw isolation* identityref
+ | +--rw max-occupancy-level? uint8
+ | +--rw path-constraints
+ | +--rw service-functions
+ | +--rw diversity
+ | +--rw diversity-type?
+ | te-types:te-path-disjointness
+
+ Figure 5: SLO SLE Templates Subtree Structure
+
+ The NSSM provides the SLO and SLE templates identifiers and
+ templates, and the common attributes of the templates are defined in
+ Section 5.1 of [RFC9543]. Standard templates provided by the
+ provider as well as custom "service-slo-sle-policy" are defined,
+ since there are many attributes defined and some attributes could
+ vary with service requirements, e.g., bandwidth or latency. A
+ customer may choose either a standard template provided by the
+ provider or a customized "service-slo-sle-policy".
+
+
+
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 10]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ 1. Standard template: The exact definition of the templates is
+ deployment specific. The attributes configuration of a standard
+ template is optional. When specifying attributes, a standard
+ template can use "template-ref" to inherit some attributes of a
+ predefined standard template and override the specific
+ attributes.
+
+ 2. Custom "service-slo-sle-policy": More description is provided in
+ Section 5.2.3.
+
+ Figure 6 shows an example where two standard network slice templates
+ are retrieved by the customers.
+
+ ========== NOTE: '\' line wrapping per RFC 8792 ===========
+
+ {
+ "ietf-network-slice-service:network-slice-services": {
+ "slo-sle-templates": {
+ "slo-sle-template": [
+ {
+ "id": "PLATINUM-template",
+ "description": "Two-way bandwidth: 1 Gbps,\
+ 95th percentile latency 50ms",
+ "slo-policy": {
+ "metric-bound": [
+ {
+ "metric-type": "two-way-bandwidth",
+ "metric-unit": "Gbps",
+ "bound": "1"
+ },
+ {
+ "metric-type": "two-way-delay-percentile",
+ "metric-unit": "milliseconds",
+ "percentile-value": "95.000",
+ "bound": "50"
+ }
+ ]
+ },
+ "sle-policy": {
+ "isolation": ["traffic-isolation"]
+ }
+ },
+ {
+ "id": "GOLD-template",
+ "description": "Two-way bandwidth: 1 Gbps,\
+ maximum latency 100ms",
+ "slo-policy": {
+ "metric-bound": [
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 11]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ {
+ "metric-type": "two-way-bandwidth",
+ "metric-unit": "Gbps",
+ "bound": "1"
+ },
+ {
+ "metric-type": "two-way-delay-maximum",
+ "metric-unit": "milliseconds",
+ "bound": "100"
+ }
+ ]
+ },
+ "sle-policy": {
+ "isolation": ["traffic-isolation"]
+ }
+ }
+ ]
+ }
+ }
+ }
+
+ Figure 6: Example of Template Retrieval
+
+ Figure 6 uses folding as defined in [RFC8792] for long lines.
+
+5.2. Network Slice Services
+
+ The "slice-service" (Figure 7) is the data structure that abstracts a
+ Network Slice Service. Each "slice-service" is uniquely identified
+ within an NSC by "id".
+
+ +--rw slice-service* [id]
+ +--rw id string
+ +--rw description? string
+ +--rw service-tags
+ | ...
+ +--rw (slo-sle-policy)?
+ | ...
+ +--rw test-only? empty
+ +--rw status
+ | ...
+ +--rw sdps
+ | ...
+ +--rw connection-groups
+ | ...
+ +--rw custom-topology
+ ...
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 12]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ Figure 7: Network Slice Service Subtree Structure
+
+ A Network Slice Service has the following main data nodes:
+
+ * "description": Provides a textual description of a Network Slice
+ Service.
+
+ * "service-tags": Indicates a management tag (e.g., "customer name"
+ ) that is used to correlate the operational information of
+ Customer Higher-level Operation System and Network Slices. It
+ might be used by a Network Slice Service provider to provide
+ additional information to an NSC during the operation of the
+ Network Slices. For example, adding tags with "customer name"
+ when multiple actual customers use the same Network Slice Service.
+ Another use case for "service-tag" might be for a provider to
+ provide additional attributes to an NSC which might be used during
+ the realization of Network Slice Services such as type of services
+ (e.g., use Layer 2 or Layer 3 technology for the realization).
+ These additional attributes can also be used by an NSC for various
+ purposes such as monitoring and assurance of the Network Slice
+ Services where the NSC can issue notifications to the customer
+ system. All these attributes are optional.
+
+ * "slo-sle-policy": Defines SLO and SLE policies for the "slice-
+ service". More details are provided in Section 5.2.3.
+
+ * "test-only": Is used to check the feasibility of the service
+ before instantiating a Network Slice Service in a network. More
+ details are provided in Section 5.2.6.
+
+ * "status": Indicates both the operational and administrative status
+ of a Network Slice Service. Mismatches between the admin/oper
+ status can be used as an indicator to detect Network Slice Service
+ anomalies.
+
+ * "sdps": Represents a set of SDPs that are involved in the Network
+ Slice Service. More details are provided in Section 5.2.1.
+
+ * "connection-groups": Abstracts the connections to the set of SDPs
+ of the Network Slice Service.
+
+ * "custom-topology": Represents custom topology constraints for the
+ Network Slice Service. More details are provided in Section 5.2.5
+
+5.2.1. Service Demarcation Points
+
+ A Network Slice Service involves two or more SDPs. A Network Slice
+ Service can be modified by adding new "sdp"s.
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 13]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ +--rw sdps
+ +--rw sdp* [id]
+ +--rw id string
+ +--rw description? string
+ +--rw geo-location
+ | ...
+ +--rw node-id? string
+ +--rw sdp-ip-address* inet:ip-address
+ +--rw tp-ref? leafref
+ +--rw service-match-criteria
+ | ...
+ +--rw incoming-qos-policy
+ | ...
+ +--rw outgoing-qos-policy
+ | ...
+ +--rw sdp-peering
+ | ...
+ +--rw ac-svc-ref*
+ | ac-svc:attachment-circuit-reference
+ +--rw ce-mode? boolean
+ +--rw attachment-circuits
+ | ...
+ +--rw status
+ | ...
+ +--ro sdp-monitoring
+ ...
+
+ Figure 8: SDP Subtree Structure
+
+ Section 5.2 of [RFC9543] describes four possible ways in which an SDP
+ may be placed:
+
+ * Within a CE
+
+ * Provider-facing ports on a CE
+
+ * Customer-facing ports on a PE
+
+ * Within a PE
+
+ Although there are four options, they can be categorized into two
+ categories: CE-based or PE-based.
+
+ In the four options, the Attachment Circuit (AC) may be part of the
+ Network Slice Service or may be external to it. Based on the AC
+ definition in Section 5.2 of [RFC9543], the customer and provider may
+ agree on a per {Network Slice Service, Connectivity Construct, and
+ SLOs/SLEs} basis to police or shape traffic on the AC in both the
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 14]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ ingress (CE to PE) direction and egress (PE to CE) direction, which
+ ensures that the traffic is within the capacity profile that is
+ agreed in a Network Slice Service. Excess traffic is dropped by
+ default, unless specific out-of-profile policies are agreed between
+ the customer and the provider.
+
+ To abstract the SDP options and SLOs/SLEs profiles, an SDP has the
+ following characteristics:
+
+ * "id": Uniquely identifies the SDP within an NSC. The identifier
+ is a string that allows any encoding for the local administration
+ of the Network Slice Service.
+
+ * "geo-location": Indicates SDP location information, which helps
+ the NSC to identify an SDP.
+
+ * "node-id": A reference to the node that hosts the SDP, which helps
+ the NSC to identify an SDP. This document assumes that the
+ Customer Higher-level Operation System can obtain the node
+ information, PE and CE, prior to the service requests. For
+ example, Service Attachment Points (SAPs) [RFC9408] can obtain PE-
+ related node information. The implementation details are left to
+ the NSC provider.
+
+ * "sdp-ip-address": The SDP IP addresses, which help the NSC to
+ identify an SDP.
+
+ * "tp-ref": A reference to a Termination Point (TP) in the custom
+ topology defined in Section 5.2.5.
+
+ * "service-match-criteria": Defines matching policies for the
+ Network Slice Service traffic to apply on a given SDP.
+
+ * "incoming-qos-policy" and "outgoing-qos-policy": Sets the incoming
+ and outgoing QoS policies to apply on a given SDP, including QoS
+ policy and specific ingress and egress traffic limits to ensure
+ access security. When applied in the incoming direction, the
+ policy is applicable to the traffic that passes through the AC
+ from the customer network or from another provider's network to
+ the Network Slice. When applied in the outgoing direction, the
+ policy is applied to the traffic from the Network Slice towards
+ the customer network or towards another provider's network. If an
+ SDP has multiple ACs, the "rate-limits" of "attachment-circuit"
+ can be set to an AC specific value, but the rate cannot exceed the
+ "rate-limits" of the SDP. If an SDP only contains a single AC,
+ then the "rate-limits" of "attachment-circuit" is the same with
+ the SDP. The definition of AC refers to Section 5.2 [RFC9543].
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 15]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ * "sdp-peering": Specifies the peers and peering protocols for an
+ SDP to exchange control-plane information, e.g., Layer 1 signaling
+ protocol or Layer 3 routing protocols, etc. As shown in Figure 9
+
+ +--rw sdp-peering
+ | +--rw peer-sap-id* string
+ | +--rw protocols
+
+ Figure 9: SDP Peering Subtree Structure
+
+ - "peer-sap-id": Indicates the references to the remote endpoints
+ of attachment circuits. This information can be used for
+ correlation purposes, such as identifying Service Attachment
+ Points (SAPs) defined in [RFC9408], which defines a model of an
+ abstract view of the provider network topology that contains
+ the points from which the services can be attached.
+
+ - "protocols": Serves as an augmentation target. Appendix A
+ shows an example where BGP and static routing are augmented to
+ the model.
+
+ * "ac-svc-ref": Refers to the ACs that have been created, which is
+ defined in Section 5.2 of
+ [I-D.ietf-opsawg-teas-attachment-circuit]. When both "ac-svc-ref"
+ and the attributes of "attachment-circuits" are defined, the "ac-
+ svc-ref" may take precedence or act as the parent AC depending on
+ the use cases.
+
+ * "ce-mode": A flag node that marks the SDP is located on the CE.
+
+ * "attachment-circuits": Specifies the list of ACs by which the
+ service traffic is received. This is an optional SDP attribute.
+ When an SDP has multiple ACs and some AC specific attributes are
+ needed, each "attachment-circuit" can specify attributes, such as
+ interface specific IP addresses, service MTU, and other
+ attributes.
+
+ * "status": Enables the control of the administrative status and
+ reporting of the operational status of the SDP. These status
+ values can be used as indicators to detect SDP anomalies.
+
+ * "sdp-monitoring": Provides SDP bandwidth statistics.
+
+ Depending on the requirements of different cases, "service-match-
+ criteria" can be used for the following purposes:
+
+ * Specify the AC type: physical or logical connection.
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 16]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ * Distinguish the SDP traffic if the SDP is located in the CE or PE.
+
+ * Distinguish the traffic of different Connection Groups (CGs) or
+ Connectivity Constructs (CCs) when multiple CGs/CCs of different
+ SLO/SLE may be set up between the same pair of SDPs, as
+ illustrated in Figure 10. Traffic needs to be explicitly mapped
+ into the Network Slice's specific Connectivity Construct. The
+ policies, "service-match-criteria", are based on the values in
+ which combination of Layer 2 and Layer 3 header and payload fields
+ within a packet to identify to which {Network Slice Service,
+ Connectivity Construct, and SLOs/SLEs} that packet is assigned.
+ For example, VLAN ([IEEE802.1Q]), C-VLAN/S-VLAN ([IEEE802.1ad]),
+ or IP addresses.
+
+ * Define specific out-of-profile policies: The customer may choose
+ to use an explicit "service-match-criteria" to map any SDP's
+ traffic or a subset of the SDP's traffic to a specific Connection
+ Group or Connectivity Construct. If a subset of traffic is
+ matched (e.g., "dscp" and/or IP addresses) and mapped to a
+ Connectivity Construct, the customer may choose to add a
+ subsequent "match-any" to explicitly map the remaining SDP traffic
+ to a separate Connectivity Construct. If the customer chooses to
+ implicitly map remaining traffic and if there are no additional
+ Connectivity Constructs where the "sdp/id" source is specified,
+ then that traffic will be dropped.
+
+ | |
+ | Slice Service 6 with 2 P2P CCs |
+ | +--x-x-x-x-x-x---->---x-x-x-x-x-x-x-x-x---+ |
+ SDP16 o / \ o SDP17
+ | \ / |
+ | +--%-%-%-%-%-%---->---%-%-%-%-%-%-%-%-%---+ |
+ | |
+ +-----------------------------------------------+
+ |<---------- Network Slice Services ----------->|
+ | between endpoints SDP16 to SDP17 |
+
+ - "x": Match DSCP
+ - "%": Match destination-ip-prefix
+
+ Figure 10: Application of Match Criteria
+
+ If an SDP is placed at the port of a CE or PE, and there is only one
+ single Connectivity Construct with a source at the SDP, traffic can
+ be implicitly mapped to this Connectivity Construct since the AC
+ information (e.g., VLAN tag) can be used to unambiguously identify
+ the traffic and the SDP is the only source of the connectivity-
+ construct. Appendix B.1 shows an example of both the implicit and
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 17]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ explicit approaches. While explicit matching is optional in some use
+ cases, it provides a more clear and readable implementation, but the
+ choice is left to the operator.
+
+ Figure 11 and Figure 12 provide examples that illustrate the use of
+ SDP options. How an NSC realizes the mapping is out of scope for
+ this document.
+
+* SDPs at customer-facing ports on the PEs: As shown in Figure 11, a
+ customer of the Network Slice Service would like to connect two
+ SDPs to satisfy specific service needs, e.g., network wholesale
+ services. In this case, the Network Slice SDPs are mapped to
+ customer-facing ports of PE nodes. The NSC uses "node-id" (PE
+ device ID), "attachment-circuits", or "ac-svc-ref" to map SDPs to
+ the customer-facing ports on the PEs.
+
+ SDP1 SDP2
+ (With PE1 parameters) (with PE2 parameters)
+ o<--------- Network Slice (NS) 1 -------->o
+ + | | +
+ + |<----------- S1 ----------->| +
+ + | | +
+ + | |<------ T1 ------>| | +
+ + v v v v +
+ + +----+ +----+ +
+ +-----+ | | PE1|==================| PE2| +-----+
+ | |----------X | | | | | |
+ | | | | | | X----------| |
+ | |----------X | | | | | |
+ +-----+ | | |==================| | | +-----+
+ AC +----+ +----+ AC
+ Customer Provider Provider Customer
+ Edge 1 Edge 1 Edge 2 Edge 2
+
+
+ Legend:
+ o: Representation of an SDP
+ +: Mapping of an SDP to customer-facing ports on the PE
+ X: Physical interfaces used for realization of the NS Service
+ S1: L0/L1/L2/L3 services used for realization of NS Service
+ T1: Tunnels used for realization of NS Service
+
+ Figure 11: An Example of SDPs Placing at PEs
+
+* SDPs within CEs: As shown in Figure 12, a customer of the Network
+ Slice Service would like to connect two SDPs to provide
+ connectivity between transport portion of 5G RAN to 5G Core
+ network functions. In this scenario, the NSC uses "node-id" (CE
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 18]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ device ID), "geo-location", "sdp-ip-address" (IP address of SDP
+ for management), "service-match-criteria" (VLAN tag), "attachment-
+ circuits" or "ac-svc-ref" (CE ACs) to map SDPs to the CE. The NSC
+ can use these CE parameters (and optionally other information to
+ uniquely identify a CE within an NSC, such as "peer-sap-id"
+ [RFC9408]) to retrieve the corresponding PE device, interface and
+ AC mapping details to complete the Network Slice Service
+ provisioning.
+
+ SDP3 SDP4
+ (With CE1 parameters) (with CE2 parameters)
+ +o<--------------- Network Slice (NS) 2 --------------->o
+ + +
+ +|<------------------------- S2 ---------------------->|+
+ +| |+
+ +| |<------ T2 ------>| |+
+ +| v v |+
+ +v +----+ +----+ v+
+ +--+--+ | | PE1|==================| PE2| | +-+---+
+ | + X----------X | | | | | + |
+ | o | | | | | X----------X o |
+ | X----------X | | | | | |
+ +-----+ | | |==================| | | +-----+
+ AC +----+ +----+ AC
+ Customer Provider Provider Customer
+ Edge 1 Edge 1 Edge 2 Edge 2
+
+
+Legend:
+ o: Representation of an SDP
+ +: Mapping of an SDP to CE
+ X: Physical interfaces used for realization of the NS Service
+S2: L0/L1/L2/L3 services used for realization of the NS Service
+T2: Tunnels used for realization of NS Service
+
+ Figure 12: An Example of SDPs Placing at CEs
+
+5.2.2. Connectivity Constructs
+
+ Section 4.2.1 of [RFC9543] defines the basic Connectivity Construct
+ (CC) and CC types of a Network Slice Service, including P2P, P2MP,
+ and A2A.
+
+ A Network Slice Service involves one or more Connectivity Constructs.
+ The "connection-groups" container is used to abstract CC, CC groups,
+ and their SLOs/SLEs policies and the structure is shown in Figure 13.
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 19]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ +--rw connection-groups
+ +--rw connection-group* [id]
+ +--rw id string
+ +--rw connectivity-type?
+ | identityref
+ +--rw (slo-sle-policy)?
+ | +--:(standard)
+ | | ...
+ | +--:(custom)
+ | ...
+ +--rw service-slo-sle-policy-override?
+ | identityref
+ +--rw connectivity-construct* [id]
+ | +--rw id
+ | | string
+ | +--rw (type)?
+ | | ...
+ | +--rw (slo-sle-policy)?
+ | | ...
+ | +--rw service-slo-sle-policy-override?
+ | | identityref
+ | +--rw status
+ | | ...
+
+ Figure 13: Connection Groups Subtree Structure
+
+ The description of the "connection-groups" data nodes is as follows:
+
+ * "connection-group": Represents a group of CCs. In the case of hub
+ and spoke connectivity of the Slice Service, it may be inefficient
+ when there are a large number of SDPs with multiple CCs. As
+ illustrated in Appendix B.3, "connectivity-type" of "ietf-vpn-
+ common:hub-spoke" and "connection-group-sdp-role" of "ietf-vpn-
+ common:hub-role" or "ietf-vpn-common:spoke-role" can be specified
+ [RFC9181]. Another use is for optimizing "slo-sle-policy"
+ configurations, treating CCs with the same SLO and SLE
+ characteristics as a Connection Group such that the Connectivity
+ Construct can inherit the SLO/SLE from the group if not explicitly
+ defined.
+
+ * "connectivity-type": Indicates the type of the Connection Group,
+ extending "vpn-common:vpn-topology" specified [RFC9181] with the
+ NS connectivity type, e.g., P2P or P2MP.
+
+ * "connectivity-construct": Represents single Connectivity
+ Construct, and "slo-sle-policy" under it represents the per-
+ Connectivity Construct SLO and SLE requirements.
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 20]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ * "slo-sle-policy" and "service-slo-sle-policy-override": The
+ details of "slo-sle-policy" are defined in Section 5.2.3.
+
+5.2.3. SLO and SLE Policy
+
+ As defined in Section 5 of [RFC9543], the SLO and SLE policy of the
+ Network Slice Services define some common attributes.
+
+ "slo-sle-policy" is used to represent these SLO and SLE policies.
+ During the creation of a Network Slice Service, the policy can be
+ specified either by a standard SLO and SLE template or a customized
+ SLO and SLE policy.
+
+ Two types of precedence rules are defined to resolve conflicts when
+ assigning policies to a Network Slice Service, Connection Group
+ "connection group", or Connectivity Construct "connectivity-
+ construct".
+
+ Scope-based Precedence: In case of conflicts, policies with a
+ narrower scope (e.g., subset of a Network Slice Service) take
+ precedence over policies with a broader scope (e.g., Network Slice
+ Service). The precedence order is as follows (from highest to lowest
+ precedence):
+
+ * Connectivity-construct at an individual sending SDP
+
+ * Connectivity-construct
+
+ * Connection-group
+
+ * Slice-level
+
+ For example, a policy assigned at the sending SDP level takes
+ precedence over a policy assigned at the connectivity-construct
+ level, which in turn takes precedence over a slice-level policy.
+ Appendix B.5 gives an example of the preceding policy, which shows a
+ Slice Service having an A2A connectivity as default and several
+ specific SLO connections.
+
+ Explicit Precedence: "service-slo-sle-policy-override" node is
+ designed to enable the complete or partial replacement of an existing
+ "slo-sle-policy" with new values for complex SLO-SLE requirements.
+ For example, if a particular "connection-group" or a "connectivity-
+ construct" has a unique bandwidth or latency setting, that are
+ different from those defined in the Slice Service, a new set of SLOs/
+ SLEs with full or partial override can be applied.
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 21]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ In a partial override, only the newly specified parameters replace
+ those in the original template, while pre-existing unspecified
+ parameters remain unchanged.
+
+ In a full override, all pre-existing parameters are removed, and a
+ new set of SLOs/SLEs is applied.
+
+ The SLO attributes include performance metric attributes,
+ availability, and MTU. The SLO structure is shown in Figure 14.
+ Figure 27 shows an example "slice5" with a custom network slice "slo-
+ policy".
+
+ +--rw slo-policy
+ | +--rw metric-bound* [metric-type]
+ | | +--rw metric-type
+ | | | identityref
+ | | +--rw metric-unit string
+ | | +--rw value-description? string
+ | | +--rw percentile-value?
+ | | | percentile
+ | | +--rw bound? uint64
+ | +--rw availability? identityref
+ | +--rw mtu? uint16
+
+ Figure 14: SLO Policy Subtree Structure
+
+ The list "metric-bound" supports the generic performance metric
+ variations and the combinations and each "metric-bound" could specify
+ a particular "metric-type". "metric-type" is defined with YANG
+ identity and supports the following options:
+
+ "one-way-bandwidth": Indicates the guaranteed minimum bandwidth
+ between any two SDPs. The bandwidth is unidirectional.
+
+ "two-way-bandwidth": Indicates the guaranteed minimum bandwidth
+ between any two SDPs. The bandwidth is bidirectional.
+
+ "shared-bandwidth": Indicates the shared SLO bandwidth bound,
+ which is the limit on the bandwidth that can be shared among a
+ group of Connectivity Constructs of a Slice Service.
+
+ "one-way-delay-maximum": Indicates the maximum one-way latency
+ between two SDPs, defined in [RFC7679].
+
+ "two-way-delay-maximum": Indicates the maximum round-trip latency
+ between two SDPs, defined in [RFC2681].
+
+ "one-way-delay-percentile": Indicates the percentile objective of
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 22]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ the one-way latency between two SDPs ( [RFC7679]).
+
+ "two-way-delay-percentile": Indicates the percentile objective of
+ the round-trip latency between two SDPs (See [RFC2681]).
+
+ "one-way-delay-variation-maximum": Indicates the jitter constraint
+ of the slice maximum permissible delay variation, and is measured
+ by the difference in the one-way latency between sequential
+ packets in a flow, as defined in [RFC3393].
+
+ "two-way-delay-variation-maximum": Indicates the jitter constraint
+ of the slice maximum permissible delay variation, and is measured
+ by the difference in the two-way latency between sequential
+ packets in a flow, as defined in [RFC3393].
+
+ "one-way-delay-variation-percentile": Indicates the percentile
+ objective of the delay variation, and is measured by the
+ difference in the one-way latency between sequential packets in a
+ flow, as defined in [RFC3393].
+
+ "two-way-delay-variation-percentile": Indicates the percentile
+ objective of the delay variation, and is measured by the
+ difference in the two-way latency between sequential packets in a
+ flow, as defined in [RFC5481].
+
+ "one-way-packet-loss": Indicates maximum permissible packet loss
+ rate (See [RFC7680], which is defined by the ratio of packets
+ dropped to packets transmitted between two SDPs.
+
+ "two-way-packet-loss": Indicates maximum permissible packet loss
+ rate (See [RFC7680], which is defined by the ratio of packets
+ dropped to packets transmitted between two SDPs.
+
+ "availability": Specifies service availability defined as the ratio
+ of uptime to the sum of uptime and downtime, where uptime is the time
+ the Network Slice is available in accordance with the SLOs associated
+ with it.
+
+ "mtu": Specifies the maximum length of Layer 2 data packets of the
+ Slice Service, in bytes. If the customer sends packets that are
+ longer than the requested service MTU, the network may discard them
+ (or for IPv4, fragment them). This service MTU takes precedence over
+ the MTUs of all ACs. The value needs to be smaller than or equal to
+ the minimum MTU value of all ACs in the SDPs.
+
+ As shown in Figure 15, the following SLEs data nodes are defined.
+
+ "security": The security leaf-list defines the list of security
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 23]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ functions that the customer requests the operator to apply to
+ traffic between the two SDPs, including authentication,
+ encryption, etc., which is defined in Section 5.1.2.1 [RFC9543].
+
+ "isolation": Specifies the isolation types that a customer
+ expects, as defined in Section 8 of [RFC9543].
+
+ "max-occupancy-level": Specifies the number of flows that the
+ operator admits (See Section 5.1.2.1 of [RFC9543]).
+
+ "path-constraints": Specifies the path constraints the customer
+ requests for the Network Slice Service, including geographic
+ restrictions and diversity which is defined in Section 5.1.2.1 of
+ [RFC9543].
+
+ +--rw sle-policy
+ +--rw security* identityref
+ +--rw isolation* identityref
+ +--rw max-occupancy-level? uint8
+ +--rw path-constraints
+ +--rw service-functions
+ +--rw diversity
+ +--rw diversity-type?
+ te-types:te-path-disjointness
+
+ Figure 15: SLE Policy Subtree Structure
+
+5.2.4. Network Slice Service Performance Monitoring
+
+ The operation and performance status of Network Slice Services is
+ also a key component of the NSSM. The model provides SLO monitoring
+ information with the following granularity:
+
+ * Per SDP: The incoming and outgoing bandwidths of an SDP are
+ specified in "sdp-monitoring" under the "sdp".
+
+ * Per Connectivity Construct: The delay, delay variation, and packet
+ loss status are specified in "connectivity-construct-monitoring"
+ under the "connectivity-construct".
+
+ * Per Connection Group: The delay, delay variation, and packet loss
+ status are specified in "connection-group-monitoring" under the
+ "connection-group".
+
+ [RFC8639] and [RFC8641] define a subscription mechanism and a push
+ mechanism for YANG datastores. These mechanisms currently allow the
+ user to subscribe to notifications on a per-client basis and specify
+ either periodic or on-demand notifications. By specifying subtree
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 24]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ filters or xpath filters to "sdp", "connectivity-construct", or
+ "connection-group", so that only interested contents will be sent.
+ The example in Figure 24 shows the way for a customer to subscribe to
+ the monitoring information for a particular Network Slice Service.
+
+ Additionally, a customer can use the NSSM to obtain a snapshot of the
+ Network Slice Service performance status through [RFC8040] or
+ [RFC6241] interfaces. For example, retrieve the per-connectivity-
+ construct data by specifying "connectivity-construct" as the filter
+ in the RESTCONF GET request.
+
+5.2.5. Custom Topology Constraints
+
+ A Slice Service customer might request for some level of control over
+ the topology or resource constraints. "custom-topology" is defined as
+ an augmentation target that references the context topology. The
+ leaf "network-ref" under this container is used to reference a
+ predefined topology as a customized topology constraint for a Network
+ Slice Service. Section 1 of [RFC8345] defines a general abstract
+ topology concept to accommodate both the provider's resource
+ capability and the customer's preferences. The abstract topology is
+ a topology that contains abstract topological elements (nodes, links,
+ and termination points).
+
+ This document defines only the minimum attributes of a custom
+ topology, which can be extended based on the implementation
+ requirements.
+
+ The following nodes are defined for the custom topology:
+
+ "custom-topology": This container serves as an augmentation target
+ for the Slice Service topology context, which can be multiple.
+ This node is located directly under the "slice-service" list.
+
+ "network-ref": This leaf is under the container "custom-topology",
+ which is defined to reference a predefined topology as a
+ customized topology constraint for a Network Slice Service, e.g.,
+ a SAP topology to request SDP feasibility checks on SAPs network
+ described in Section 3 of [RFC9408], an abstract Traffic
+ Engineering (TE) topology defined in Section 3.13 of [RFC8795] to
+ customize the service paths in a Network Slice Service.
+
+ "tp-ref": A reference to a Termination Point (TP) in the custom
+ topology, under the list "sdp", can be used to associate an SDP
+ with a TP of the customized topology. The example TPs could be
+ parent termination points of the SAP topology.
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 25]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+5.2.6. Network Slice Service Feasibility Check
+
+ A Network Slice Service customer may request to check the feasibility
+ of a request before instantiating or modifying a Network Slice
+ Service, e.g., network resources such as service access points for
+ service delivery. In such a case, this document introduces a "test-
+ only" mode (semantics derived from NETCONF [RFC6241] test operation),
+ which differs from standard management operations.
+
+ A "test-only" Network Slice Service is configured as usual with the
+ associated per slice SLOs/SLEs. The NSC computes the feasible
+ Connectivity Constructs to the configured SLOs/SLEs. This
+ computation does not create the Network Slice or reserve any
+ resources in the provider's network, it simply computes the resulting
+ Network Slice based on the request. The Network Slice "admin-status"
+ and the Connection Groups or Connectivity Construct list are used to
+ convey the result. For example, "admin-up" can be used to indicate a
+ status of success. Customers can query the "test-only" Connectivity
+ Constructs attributes, or can subscribe to be notified when the
+ Connectivity Constructs status change. If the check fails, the
+ feedback is conveyed through the "rejected" value of "admin-status",
+ indicating the reasons for the failure, such as insufficient
+ resources or constraint violation.
+
+ As defined in Section 6.3 of [RFC9543] for multi-domain requirements,
+ when a Network Slice spans multiple administrative domains, the
+ 'test-only' mode relies on the NSC to aggregate and validate
+ information across these domains. This could include:
+
+ 1. Validating end-to-end Network Slice requests to ensure they can
+ be realized across all domains.
+
+ 2. Checking resource availability and constraints within each domain
+ to confirm feasibility.
+
+ 3. Identifying potential conflicts or bottlenecks between domains
+ that may impact the Network Slice's performance or realization.
+
+ The "test-only" applies only if the data model is used with a
+ protocol that does not intrinsically support such operation, e.g.,
+ [RFC8040]. When using NETCONF, the "test-only" value of the parameter in the operation (Section 7.2 of
+ [RFC6241]) also applies.
+
+
+
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 26]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ +--------+ +--------+
+ |customer| | NSC |
+ +--------+ +--------+
+ | |
+ | Request a Network Slice Service |
+ | feasibility check ("test-only") |
+ |---------------------------------------->| Validate the NS
+ | | based on
+ | | SDPs and
+ | | SLOs/SLEs
+ | Validated NS and status |
+ |<----------------------------------------|
+ | |
+
+ NS: Network Slice
+
+ Figure 16: An Example of NSS Feasibility Check
+
+6. Network Slice Service Module
+
+ The "ietf-network-slice-service" module uses types defined in
+ [RFC6991], [RFC8294], [RFC8345], [RFC8519], [RFC9179], [RFC9181],
+ [I-D.ietf-opsawg-teas-attachment-circuit],
+ [I-D.ietf-opsawg-teas-common-ac], and [I-D.ietf-teas-rfc8776-update].
+
+ file "ietf-network-slice-service@2025-05-09.yang"
+ module ietf-network-slice-service {
+ yang-version 1.1;
+ namespace
+ "urn:ietf:params:xml:ns:yang:ietf-network-slice-service";
+ prefix ietf-nss;
+
+ import ietf-inet-types {
+ prefix inet;
+ reference
+ "RFC 6991: Common YANG Types";
+ }
+ import ietf-routing-types {
+ prefix rt-types;
+ reference
+ "RFC 8294: Common YANG Data Types for the Routing Area";
+ }
+ import ietf-yang-types {
+ prefix yang;
+ reference
+ "RFC 6991: Common YANG Data Types";
+ }
+ import ietf-geo-location {
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 27]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ prefix geo;
+ reference
+ "RFC 9179: A YANG Grouping for Geographic Locations";
+ }
+ import ietf-vpn-common {
+ prefix vpn-common;
+ reference
+ "RFC 9181: A Common YANG Data Model for Layer 2 and Layer 3
+ VPNs";
+ }
+ import ietf-network {
+ prefix nw;
+ reference
+ "RFC 8345: A YANG Data Model for Network Topologies";
+ }
+ import ietf-network-topology {
+ prefix nt;
+ reference
+ "RFC 8345: A YANG Data Model for Network
+ Topologies, Section 6.2";
+ }
+ import ietf-ac-common {
+ prefix ac-common;
+ reference
+ "RFC BBBB: A Common YANG Data Model for Attachment Circuits";
+ }
+ import ietf-ac-svc {
+ prefix ac-svc;
+ reference
+ "RFC CCCC: YANG Data Models for Bearers and 'Attachment
+ Circuits'-as-a-Service (ACaaS)";
+ }
+ import ietf-te-types {
+ prefix te-types;
+ reference
+ "RFC DDDD: Common YANG Types for Traffic Engineering";
+ }
+ import ietf-te-packet-types {
+ prefix te-packet-types;
+ reference
+ "RFC DDDD: Common YANG Data Types for Traffic Engineering";
+ }
+
+ organization
+ "IETF Traffic Engineering Architecture and Signaling (TEAS)
+ Working Group";
+ contact
+ "WG Web:
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 28]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ WG List:
+
+ Editor: Bo Wu
+
+ Editor: Dhruv Dhody
+
+ Editor: Reza Rokui
+
+ Editor: Tarek Saad
+
+ Editor: John Mullooly
+ ";
+ description
+ "This YANG module defines a service model for the RFC 9543
+ Network Slice Service.
+
+ Copyright (c) 2025 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject to
+ the license terms contained in, the Revised BSD License set
+ forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (https://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC AAAA; see the
+ RFC itself for full legal notices.";
+
+ revision 2025-05-09 {
+ description
+ "Initial revision.";
+ reference
+ "RFC AAAA: A YANG Data Model for the RFC 9543 Network Slice
+ Service";
+ }
+
+ /* Identities */
+
+ identity service-tag-type {
+ description
+ "Base identity of Network Slice Service tag type, which is
+ used for management purposes, such as classification
+ (e.g., customer names) and policy constraints
+ (e.g., Layer 2 or Layer 3 technology realization).";
+ }
+
+ identity customer {
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 29]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ base service-tag-type;
+ description
+ "The Network Slice Service customer name tag type,
+ e.g., adding tags with 'customer name' when multiple actual
+ customers use the same Network Slice Service.";
+ }
+
+ identity service {
+ base service-tag-type;
+ description
+ "The Network Slice Service tag type, which can indicate the
+ technical constraints used during service realization
+ (for example, Layer 2 or Layer 3 technologies).";
+ }
+
+ identity opaque {
+ base service-tag-type;
+ description
+ "An opaque type, which can be used for future use,
+ such as filtering of services.";
+ }
+
+ identity attachment-circuit-tag-type {
+ description
+ "Base identity for the Attachment Circuit tag type.";
+ }
+
+ identity vlan-id {
+ base attachment-circuit-tag-type;
+ description
+ "Identity for VLAN ID tag type, 802.1Q dot1Q.";
+ reference
+ "IEEE Std 802.1Q: IEEE Standard for Local and Metropolitan
+ Area Networks--Bridges and Bridged
+ Networks";
+ }
+
+ identity cvlan-id {
+ base attachment-circuit-tag-type;
+ description
+ "Identity for C-VLAN ID tag type, 802.1ad QinQ VLAN IDs.";
+ reference
+ "IEEE Std 802.1ad: IEEE Standard for Local and Metropolitan
+ Area Networks---Virtual Bridged Local
+ Area Networks---Amendment 4: Provider
+ Bridges";
+ }
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 30]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ identity svlan-id {
+ base attachment-circuit-tag-type;
+ description
+ "Identity for S-VLAN ID tag type, 802.1ad QinQ VLAN IDs.";
+ reference
+ "IEEE Std 802.1ad: IEEE Standard for Local and Metropolitan
+ Area Networks---Virtual Bridged Local
+ Area Networks---Amendment 4: Provider
+ Bridges";
+ }
+
+ identity ip-address-mask {
+ base attachment-circuit-tag-type;
+ description
+ "Identity for IP address mask tag type.";
+ }
+
+ identity service-isolation-type {
+ description
+ "Base identity for Network Slice Service isolation type.";
+ }
+
+ identity traffic-isolation {
+ base service-isolation-type;
+ description
+ "Specify the requirement for separating the traffic of the
+ customer's Network Slice Service from other services,
+ which may be provided by the service provider using VPN
+ technologies, such as L3VPN, L2VPN, EVPN, or others.";
+ }
+
+ identity service-security-type {
+ description
+ "Base identity for Network Slice Service security type.";
+ }
+
+ identity authentication {
+ base service-security-type;
+ description
+ "Indicates that the Slice Service requires authentication.";
+ }
+
+ identity integrity {
+ base service-security-type;
+ description
+ "Indicates that the Slice Service requires data integrity.";
+ }
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 31]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ identity encryption {
+ base service-security-type;
+ description
+ "Indicates that the Slice Service requires data encryption.";
+ }
+
+ identity point-to-point {
+ base vpn-common:vpn-topology;
+ description
+ "Identity for point-to-point Network Slice
+ Service connectivity.";
+ }
+
+ identity point-to-multipoint {
+ base vpn-common:vpn-topology;
+ description
+ "Identity for point-to-multipoint Network Slice
+ Service connectivity.";
+ }
+
+ identity multipoint-to-multipoint {
+ base vpn-common:vpn-topology;
+ description
+ "Identity for multipoint-to-multipoint Network Slice
+ Service connectivity.";
+ }
+
+ identity multipoint-to-point {
+ base vpn-common:vpn-topology;
+ description
+ "Identity for multipoint-to-point Network Slice
+ Service connectivity.";
+ }
+
+ identity sender-role {
+ base vpn-common:role;
+ description
+ "Indicates that an SDP is acting as a sender.";
+ }
+
+ identity receiver-role {
+ base vpn-common:role;
+ description
+ "Indicates that an SDP is acting as a receiver.";
+ }
+
+ identity service-slo-metric-type {
+ description
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 32]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "Base identity for Network Slice Service SLO metric type.";
+ }
+
+ identity one-way-bandwidth {
+ base service-slo-metric-type;
+ description
+ "SLO bandwidth metric. Minimum guaranteed bandwidth between
+ two SDPs at any time and is measured unidirectionally.";
+ }
+
+ identity two-way-bandwidth {
+ base service-slo-metric-type;
+ description
+ "SLO bandwidth metric. Minimum guaranteed bandwidth between
+ two SDPs at any time.";
+ }
+
+ identity shared-bandwidth {
+ base service-slo-metric-type;
+ description
+ "The shared SLO bandwidth bound. It is the limit on the
+ bandwidth that can be shared among a group of
+ Connectivity Constructs of a Slice Service.";
+ }
+
+ identity one-way-delay-maximum {
+ base service-slo-metric-type;
+ description
+ "The SLO objective of this metric is the upper bound of network
+ delay when transmitting between two SDPs.";
+ reference
+ "RFC 7679: A One-Way Delay Metric for IP Performance
+ Metrics (IPPM)";
+ }
+
+ identity one-way-delay-percentile {
+ base service-slo-metric-type;
+ description
+ "The SLO objective of this metric is percentile objective of
+ network delay when transmitting between two SDPs.
+ The metric is defined in RFC7679.";
+ reference
+ "RFC 7679: A One-Way Delay Metric for IP Performance
+ Metrics (IPPM)";
+ }
+
+ identity two-way-delay-maximum {
+ base service-slo-metric-type;
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 33]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ description
+ "SLO two-way delay is the upper bound of network delay when
+ transmitting between two SDPs";
+ reference
+ "RFC 2681: A Round-trip Delay Metric for IPPM";
+ }
+
+ identity two-way-delay-percentile {
+ base service-slo-metric-type;
+ description
+ "The SLO objective of this metric is the percentile
+ objective of network delay when the traffic transmitting
+ between two SDPs.";
+ reference
+ "RFC 2681: A Round-trip Delay Metric for IPPM";
+ }
+
+ identity one-way-delay-variation-maximum {
+ base service-slo-metric-type;
+ description
+ "The SLO objective of this metric is maximum bound of the
+ difference in the one-way delay between sequential packets
+ between two SDPs.";
+ reference
+ "RFC 3393: IP Packet Delay Variation Metric for IP Performance
+ Metrics (IPPM)";
+ }
+
+ identity one-way-delay-variation-percentile {
+ base service-slo-metric-type;
+ description
+ "The SLO objective of this metric is the percentile objective
+ in the one-way delay between sequential packets between two
+ SDPs.";
+ reference
+ "RFC 3393: IP Packet Delay Variation Metric for IP Performance
+ Metrics (IPPM)";
+ }
+
+ identity two-way-delay-variation-maximum {
+ base service-slo-metric-type;
+ description
+ "SLO two-way delay variation is the difference in the
+ round-trip delay between sequential packets between two
+ SDPs.";
+ reference
+ "RFC 5481: Packet Delay Variation Applicability Statement";
+ }
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 34]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ identity two-way-delay-variation-percentile {
+ base service-slo-metric-type;
+ description
+ "The SLO objective of this metric is the percentile objective
+ in the round-trip delay between sequential packets between
+ two SDPs.";
+ reference
+ "RFC 5481: Packet Delay Variation Applicability Statement";
+ }
+
+ identity one-way-packet-loss {
+ base service-slo-metric-type;
+ description
+ "This metric type refers to the ratio of packets dropped
+ to packets transmitted between two SDPs in one-way.";
+ reference
+ "RFC 7680: A One-Way Loss Metric for IP Performance
+ Metrics (IPPM)";
+ }
+
+ identity two-way-packet-loss {
+ base service-slo-metric-type;
+ description
+ "This metric type refers to the ratio of packets dropped
+ to packets transmitted between two SDPs in two-way.";
+ reference
+ "RFC 7680: A One-Way Loss Metric for IP Performance
+ Metrics (IPPM)";
+ }
+
+ identity availability-type {
+ description
+ "Base identity for availability.";
+ }
+
+ identity six-nines {
+ base availability-type;
+ description
+ "Specifies the availability level: 99.9999%";
+ }
+
+ identity five-nines {
+ base availability-type;
+ description
+ "Specifies the availability level: 99.999%";
+ }
+
+ identity four-nines {
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 35]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ base availability-type;
+ description
+ "Specifies the availability level: 99.99%";
+ }
+
+ identity three-nines {
+ base availability-type;
+ description
+ "Specifies the availability level: 99.9%";
+ }
+
+ identity two-nines {
+ base availability-type;
+ description
+ "Specifies the availability level: 99%";
+ }
+
+ identity service-match-type {
+ description
+ "Base identity for Network Slice Service traffic
+ match type.";
+ }
+
+ identity phy-interface {
+ base service-match-type;
+ description
+ "Uses the physical interface as match criteria for
+ Slice Service traffic.";
+ }
+
+ identity vlan {
+ base service-match-type;
+ description
+ "Uses the VLAN ID as match criteria for the Slice Service
+ traffic.";
+ }
+
+ identity label {
+ base service-match-type;
+ description
+ "Uses the MPLS label as match criteria for the Slice Service
+ traffic.";
+ }
+
+ identity source-ip-prefix {
+ base service-match-type;
+ description
+ "Uses source IP prefix as match criteria for the Slice Service
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 36]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ traffic. Examples of 'value' of this match type are
+ '192.0.2.0/24' and '2001:db8::1/64'.";
+ }
+
+ identity destination-ip-prefix {
+ base service-match-type;
+ description
+ "Uses destination IP prefix as match criteria for the Slice
+ Service traffic. Examples of 'value' of this match type are
+ '203.0.113.1/32' and '2001:db8::2/128'.";
+ }
+
+ identity dscp {
+ base service-match-type;
+ description
+ "Uses DSCP field in the IP packet header as match criteria
+ for the Slice Service traffic.";
+ }
+
+ identity acl {
+ base service-match-type;
+ description
+ "Uses Access Control List (ACL) as match criteria
+ for the Slice Service traffic.";
+ reference
+ "RFC 8519: YANG Data Model for Network Access Control
+ Lists (ACLs)";
+ }
+
+ identity any {
+ base service-match-type;
+ description
+ "Matches any Slice Service traffic.";
+ }
+
+ identity slo-sle-policy-override {
+ description
+ "Base identity for SLO/SLE policy override options.";
+ }
+
+ identity full-override {
+ base slo-sle-policy-override;
+ description
+ "The SLO/SLE policy defined at the child level overrides a
+ parent SLO/SLE policy, which means that no SLO/SLEs are
+ inherited from parent if a child SLO/SLE policy exists.";
+ }
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 37]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ identity partial-override {
+ base slo-sle-policy-override;
+ description
+ "The SLO/SLE policy defined at the child level updates the
+ parent SLO/SLE policy. For example, if a specific SLO is
+ defined at the child level, that specific SLO overrides
+ the one inherited from a parent SLO/SLE policy, while all
+ other SLOs in the parent SLO-SLE policy still apply.";
+ }
+
+ /* Typedef */
+
+ typedef percentage {
+ type uint8 {
+ range "0..100";
+ }
+ description
+ "Integer indicating a percentage value.";
+ }
+
+ typedef percentile {
+ type decimal64 {
+ fraction-digits 3;
+ range "0..100";
+ }
+ description
+ "The percentile is a value between 0 and 100
+ to 3 decimal places, e.g., 10.000, 99.900,99.990, etc.
+ For example, for a given one-way delay measurement,
+ if the percentile is set to 95.000 and the 95th percentile
+ one-way delay is 2 milliseconds, then the 95 percent of
+ the sample value is less than or equal to 2 milliseconds.";
+ }
+
+ typedef ns-compute-status {
+ type te-types:te-common-status;
+ description
+ "A type definition for representing the Network Slice
+ compute status. Note that all statuses apart from up and down
+ are considered as unknown.";
+ }
+
+ typedef slice-template-ref {
+ type leafref {
+ path "/ietf-nss:network-slice-services"
+ + "/ietf-nss:slo-sle-templates"
+ + "/ietf-nss:slo-sle-template"
+ + "/ietf-nss:id";
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 38]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ }
+ description
+ "This type is used by data models that need to reference
+ Network Slice templates.";
+ }
+
+ typedef slice-service-ref {
+ type leafref {
+ path
+ "/ietf-nss:network-slice-services/ietf-nss:slice-service"
+ + "/ietf-nss:id";
+ }
+ description
+ "Defines a reference to a slice service that can be used
+ by other modules.";
+ }
+
+ /* Groupings */
+
+ grouping service-slos {
+ description
+ "A reusable grouping for directly measurable objectives of
+ a Slice Service.";
+ container slo-policy {
+ description
+ "Contains the SLO policy.";
+ list metric-bound {
+ key "metric-type";
+ description
+ "List of Slice Service metric bounds.";
+ leaf metric-type {
+ type identityref {
+ base service-slo-metric-type;
+ }
+ description
+ "Identifies SLO metric type of the Slice Service.";
+ }
+ leaf metric-unit {
+ type string;
+ mandatory true;
+ description
+ "The metric unit of the parameter. For example,
+ for time units, where the options are hours, minutes,
+ seconds, milliseconds, microseconds, and nanoseconds;
+ for bandwidth units, where the options are bps, Kbps,
+ Mbps, Gbps; for the packet loss rate unit,
+ the options can be a percentage.";
+ }
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 39]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ leaf value-description {
+ type string;
+ description
+ "The description of the provided value.";
+ }
+ leaf percentile-value {
+ type percentile;
+ description
+ "The percentile value of the metric type.";
+ }
+ leaf bound {
+ type uint64;
+ description
+ "The bound on the Slice Service connection metric.
+ When set to zero, this indicates an unbounded
+ upper limit for the specific metric-type.";
+ }
+ }
+ leaf availability {
+ type identityref {
+ base availability-type;
+ }
+ description
+ "Service availability level.";
+ }
+ leaf mtu {
+ type uint32;
+ units "bytes";
+ description
+ "Specifies the maximum length of Layer 2 data
+ packets of the Slice Service.
+ If the customer sends packets that are longer than the
+ requested service MTU, the network may discard them
+ (or for IPv4, fragment them).
+ This service MTU takes precedence over the MTUs of
+ all Attachment Circuits (ACs). The value needs to be
+ less than or equal to the minimum MTU value of
+ all ACs in the SDPs.";
+ }
+ }
+ }
+
+ grouping service-sles {
+ description
+ "A reusable grouping for indirectly measurable objectives of
+ a Slice Service.";
+ container sle-policy {
+ description
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 40]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "Contains the SLE policy.";
+ leaf-list security {
+ type identityref {
+ base service-security-type;
+ }
+ description
+ "The security functions (e.g., `authentication` and
+ `encryption`) that the customer requests the operator to
+ apply to traffic between the two SDPs.";
+ }
+ leaf-list isolation {
+ type identityref {
+ base service-isolation-type;
+ }
+ description
+ "The Slice Service isolation requirement.";
+ }
+ leaf max-occupancy-level {
+ type uint8 {
+ range "1..100";
+ }
+ description
+ "The maximal occupancy level specifies the number of flows
+ to be admitted and optionally a maximum number of
+ countable resource units (e.g., IP or MAC addresses)
+ a Network Slice Service can consume.";
+ }
+ container path-constraints {
+ description
+ "Container for the policy of path constraints
+ applicable to the Slice Service.";
+ container service-functions {
+ description
+ "Container for the policy of service function
+ applicable to the Slice Service.";
+ }
+ container diversity {
+ description
+ "Container for the policy of disjointness
+ applicable to the Slice Service.";
+ leaf diversity-type {
+ type te-types:te-path-disjointness;
+ description
+ "The type of disjointness on Slice Service, i.e.,
+ across all Connectivity Constructs.";
+ }
+ }
+ }
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 41]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ }
+ }
+
+ grouping slice-service-template {
+ description
+ "A reusable grouping for Slice Service templates.";
+ container slo-sle-templates {
+ description
+ "Contains a set of Slice Service templates.";
+ list slo-sle-template {
+ key "id";
+ description
+ "List for SLO and SLE template identifiers.";
+ leaf id {
+ type string;
+ description
+ "Identification of the Service Level Objective (SLO)
+ and Service Level Expectation (SLE) template to be used.
+ Local administration meaning.";
+ }
+ leaf description {
+ type string;
+ description
+ "Describes the SLO and SLE policy template.";
+ }
+ leaf template-ref {
+ type slice-template-ref;
+ description
+ "The reference to a standard template. When set it
+ indicates the base template over which further
+ SLO/SLE policy changes are made.";
+ }
+ uses service-slos;
+ uses service-sles;
+ }
+ }
+ }
+
+ grouping service-slo-sle-policy {
+ description
+ "Slice service policy grouping.";
+ choice slo-sle-policy {
+ description
+ "Choice for SLO and SLE policy template.
+ Can be standard template or customized template.";
+ case standard {
+ description
+ "Standard SLO template.";
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 42]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ leaf slo-sle-template {
+ type slice-template-ref;
+ description
+ "Standard SLO and SLE template to be used.";
+ }
+ }
+ case custom {
+ description
+ "Customized SLO and SLE template.";
+ container service-slo-sle-policy {
+ description
+ "Contains the SLO and SLE policy.";
+ leaf description {
+ type string;
+ description
+ "Describes the SLO and SLE policy.";
+ }
+ uses service-slos;
+ uses service-sles;
+ }
+ }
+ }
+ }
+
+ grouping service-qos {
+ description
+ "Grouping for the Slice Service QoS policy.";
+ container incoming-qos-policy {
+ description
+ "The QoS policy imposed on ingress direction of the traffic,
+ from the customer network or from another provider's
+ network.";
+ leaf qos-policy-name {
+ type string;
+ description
+ "The name of the QoS policy that is applied to the
+ Attachment Circuit. The name can reference a QoS
+ profile that is pre-provisioned on the device.";
+ }
+ container rate-limits {
+ description
+ "Container for the asymmetric traffic control.";
+ uses ac-common:bandwidth-parameters;
+ container classes {
+ description
+ "Container for service class bandwidth control.";
+ list cos {
+ key "cos-id";
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 43]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ description
+ "List of Class of Services.";
+ leaf cos-id {
+ type uint8;
+ description
+ "Identifier of the CoS, indicated by
+ a Differentiated Services Code Point
+ (DSCP) or a CE-CLAN CoS (802.1p)
+ value in the service frame.";
+ reference
+ "IEEE Std 802.1Q: Bridges and Bridged
+ Networks";
+ }
+ uses ac-common:bandwidth-parameters;
+ }
+ }
+ }
+ }
+ container outgoing-qos-policy {
+ description
+ "The QoS policy imposed on egress direction of the traffic,
+ towards the customer network or towards another
+ provider's network.";
+ leaf qos-policy-name {
+ type string;
+ description
+ "The name of the QoS policy that is applied to the
+ Attachment Circuit. The name can reference a QoS
+ profile that is pre-provisioned on the device.";
+ }
+ container rate-limits {
+ description
+ "The rate-limit imposed on outgoing traffic.";
+ uses ac-common:bandwidth-parameters;
+ container classes {
+ description
+ "Container for classes.";
+ list cos {
+ key "cos-id";
+ description
+ "List of Class of Services.";
+ leaf cos-id {
+ type uint8;
+ description
+ "Identifier of the CoS, indicated by
+ a Differentiated Services Code Point
+ (DSCP) or a CE-CLAN CoS (802.1p)
+ value in the service frame.";
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 44]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ reference
+ "IEEE Std 802.1Q: Bridges and Bridged
+ Networks";
+ }
+ uses ac-common:bandwidth-parameters;
+ }
+ }
+ }
+ }
+ }
+
+ grouping service-slo-sle-policy-override {
+ description
+ "Slice Service policy override grouping.";
+ leaf service-slo-sle-policy-override {
+ type identityref {
+ base slo-sle-policy-override;
+ }
+ description
+ "SLO/SLE policy override option.";
+ }
+ }
+
+ grouping connectivity-construct-monitoring-metrics {
+ description
+ "Grouping for Connectivity Construct monitoring metrics.";
+ uses
+ te-packet-types:one-way-performance-metrics-gauge-packet;
+ uses
+ te-packet-types:two-way-performance-metrics-gauge-packet;
+ }
+
+ /* Main Network Slice Services Container */
+
+ container network-slice-services {
+ description
+ "Contains a list of Network Slice Services";
+ uses slice-service-template;
+ list slice-service {
+ key "id";
+ description
+ "A Slice Service is identified by a service id.";
+ leaf id {
+ type string;
+ description
+ "A unique Slice Service identifier within an NSC.";
+ }
+ leaf description {
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 45]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ type string;
+ description
+ "Textual description of the Slice Service.";
+ }
+ container service-tags {
+ description
+ "Container for a list of service tags for management
+ purposes, such as policy constraints
+ (e.g., Layer 2 or Layer 3 technology realization),
+ classification (e.g., customer names, opaque values).";
+ list tag-type {
+ key "tag-type";
+ description
+ "The service tag list.";
+ leaf tag-type {
+ type identityref {
+ base service-tag-type;
+ }
+ description
+ "Slice Service tag type, e.g., realization technology
+ constraints, customer name, or other customer-defined
+ opaque types.";
+ }
+ leaf-list tag-type-value {
+ type string;
+ description
+ "The tag values, e.g., 5G customer names when multiple
+ customers share the same Slice Service in 5G scenario,
+ or Slice realization technology (such as Layer 2 or
+ Layer 3).";
+ }
+ }
+ }
+ uses service-slo-sle-policy;
+ leaf test-only {
+ type empty;
+ description
+ "When present, this is a feasibility check. That is, no
+ resources are reserved in the network.";
+ }
+ uses ac-common:service-status;
+ container sdps {
+ description
+ "Slice Service SDPs.";
+ list sdp {
+ key "id";
+ min-elements 2;
+ description
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 46]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "List of SDPs in this Slice Service.";
+ leaf id {
+ type string;
+ description
+ "The unique identifier of the SDP within the scope of
+ an NSC.";
+ }
+ leaf description {
+ type string;
+ description
+ "Provides a description of the SDP.";
+ }
+ uses geo:geo-location;
+ leaf node-id {
+ type string;
+ description
+ "A unique identifier of an edge node of the SDP
+ within the scope of the NSC.";
+ }
+ leaf-list sdp-ip-address {
+ type inet:ip-address;
+ description
+ "IPv4 or IPv6 address of the SDP.";
+ }
+ leaf tp-ref {
+ type leafref {
+ path
+ "/nw:networks/nw:network[nw:network-id="
+ + "current()/../../../custom-topology/network-ref]/"
+ + "nw:node/nt:termination-point/nt:tp-id";
+ }
+ description
+ "A reference to Termination Point (TP) in the custom
+ topology";
+ reference
+ "RFC 8345: A YANG Data Model for Network Topologies";
+ }
+ container service-match-criteria {
+ description
+ "Describes the Slice Service match criteria.";
+ list match-criterion {
+ key "index";
+ description
+ "List of the Slice Service traffic match criteria.";
+ leaf index {
+ type uint32;
+ description
+ "The identifier of a match criteria.";
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 47]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ }
+ list match-type {
+ key "type";
+ description
+ "List of the Slice Service traffic match types.";
+ leaf type {
+ type identityref {
+ base service-match-type;
+ }
+ description
+ "Indicates the match type of the entry in the
+ list of the Slice Service match criteria.";
+ }
+ choice value {
+ description
+ "Choice for value of the match type.";
+ case interface {
+ when "derived-from-or-self"
+ + "(type,'ietf-nss:phy-interface')" {
+ description
+ "Match type is a physical interface.";
+ }
+ leaf-list interface-name {
+ type string;
+ description
+ "Physical interface name for the
+ match criteria.";
+ }
+ }
+ case vlan {
+ when "derived-from-or-self"
+ + "(type, 'ietf-nss:vlan')" {
+ description
+ "Match type is a VLAN ID.";
+ }
+ leaf-list vlan {
+ type uint16 {
+ range "0..4095";
+ }
+ description
+ "VLAN ID value for the match criteria.";
+ }
+ }
+ case label {
+ when "derived-from-or-self"
+ + "(type, 'ietf-nss:label')" {
+ description
+ "Match type is an MPLS label.";
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 48]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ }
+ leaf-list label {
+ type rt-types:mpls-label;
+ description
+ "MPLS label value for the match
+ criteria.";
+ }
+ }
+ case ip-prefix {
+ when
+ "derived-from-or-self"
+ + "(type, 'ietf-nss:source-ip-prefix') or "
+ + "derived-from-or-self"
+ + "(type, 'ietf-nss:destination-ip-prefix')" {
+ description
+ "Match type is an IP prefix.";
+ }
+ leaf-list ip-prefix {
+ type inet:ip-prefix;
+ description
+ "IP prefix value for the match criteria.";
+ }
+ }
+ case dscp {
+ when "derived-from-or-self"
+ + "(type, 'ietf-nss:dscp')" {
+ description
+ "Match type is a DSCP value.";
+ }
+ leaf-list dscp {
+ type inet:dscp;
+ description
+ "DSCP value for the match criteria.";
+ }
+ }
+ case acl {
+ when "derived-from-or-self"
+ + "(type, 'ietf-nss:acl')" {
+ description
+ "Match type is an ACL name.";
+ }
+ leaf-list acl-name {
+ type string {
+ length "1..64";
+ }
+ description
+ "ACL name value for the match
+ criteria.";
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 49]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ }
+ }
+ /* Add more cases as needed for other
+ match types */
+ }
+ }
+ leaf target-connection-group-id {
+ type leafref {
+ path
+ "../../../../../ietf-nss:connection-groups"
+ + "/ietf-nss:connection-group"
+ + "/ietf-nss:id";
+ }
+ mandatory true;
+ description
+ "Reference to the Slice Service Connection Group.";
+ }
+ leaf connection-group-sdp-role {
+ type identityref {
+ base vpn-common:role;
+ }
+ default "vpn-common:any-to-any-role";
+ description
+ "Specifies the role of SDP in the Connection Group
+ When the service connection type is MP2MP,
+ such as hub and spoke service connection type.
+ In addition, this helps to create Connectivity
+ Construct automatically, rather than explicitly
+ specifying each one.";
+ }
+ leaf target-connectivity-construct-id {
+ type leafref {
+ path
+ "../../../../../ietf-nss:connection-groups"
+ + "/ietf-nss:connection-group[ietf-nss:id="
+ + "current()/../target-connection-group-id]"
+ + "/ietf-nss:connectivity-construct/ietf-nss:id";
+ }
+ description
+ "Reference to a Network Slice Connectivity
+ Construct.";
+ }
+ }
+ }
+ uses service-qos;
+ container sdp-peering {
+ description
+ "Describes SDP peering attributes.";
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 50]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ leaf-list peer-sap-id {
+ type string;
+ description
+ "Indicates the reference to the remote endpoints of
+ the Attachment Circuits. This information can be
+ used for correlation purposes, such as identifying
+ SAPs of provider equipments when requesting
+ a service with CE based SDP attributes.";
+ reference
+ "RFC 9408: A YANG Network Data Model for Service
+ Attachment Points (SAPs)";
+ }
+ container protocols {
+ description
+ "Serves as an augmentation target.
+ Protocols can be augmented into this container,
+ e.g., BGP or static routing.";
+ }
+ }
+ leaf-list ac-svc-ref {
+ type ac-svc:attachment-circuit-reference;
+ description
+ "A reference to the ACs that have been created before
+ the slice creation.";
+ reference
+ "RFC CCCC: YANG Data Models for Bearers and
+ 'Attachment Circuits'-as-a-Service (ACaaS)";
+ }
+ leaf ce-mode {
+ type boolean;
+ description
+ "When set to 'true', this indicates the SDP is located
+ on the CE.";
+ }
+ container attachment-circuits {
+ description
+ "List of Attachment Circuits.";
+ list attachment-circuit {
+ key "id";
+ description
+ "The Network Slice Service SDP Attachment Circuit
+ related parameters.";
+ leaf id {
+ type string;
+ description
+ "The identifier of Attachment Circuit.";
+ }
+ leaf description {
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 51]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ type string;
+ description
+ "The Attachment Circuit's description.";
+ }
+ leaf ac-svc-ref {
+ type ac-svc:attachment-circuit-reference;
+ description
+ "A reference to the AC service that has been
+ created before the slice creation.";
+ reference
+ "RFC CCCC: YANG Data Models for Bearers and
+ 'Attachment Circuits'-as-a-Service (ACaaS)";
+ }
+ leaf ac-node-id {
+ type string;
+ description
+ "The Attachment Circuit node ID in the case of
+ multi-homing.";
+ }
+ leaf ac-tp-id {
+ type string;
+ description
+ "The termination port ID of the
+ Attachment Circuit.";
+ }
+ leaf ac-ipv4-address {
+ type inet:ipv4-address;
+ description
+ "The IPv4 address of the AC.";
+ }
+ leaf ac-ipv4-prefix-length {
+ type uint8 {
+ range "0..32";
+ }
+ description
+ "The length of the IPv4 subnet prefix.";
+ }
+ leaf ac-ipv6-address {
+ type inet:ipv6-address;
+ description
+ "The IPv6 address of the AC.";
+ }
+ leaf ac-ipv6-prefix-length {
+ type uint8 {
+ range "0..128";
+ }
+ description
+ "The length of IPv6 subnet prefix.";
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 52]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ }
+ leaf mtu {
+ type uint32;
+ units "bytes";
+ description
+ "Maximum size of the Slice Service Layer 2 data
+ packet that can traverse an SDP.";
+ }
+ container ac-tags {
+ description
+ "Container for the Attachment Circuit tags.";
+ list ac-tag {
+ key "tag-type";
+ description
+ "The Attachment Circuit tag list.";
+ leaf tag-type {
+ type identityref {
+ base attachment-circuit-tag-type;
+ }
+ description
+ "The Attachment Circuit tag type.";
+ }
+ leaf-list tag-type-value {
+ type string;
+ description
+ "The Attachment Circuit tag values.
+ For example, the tag may indicate
+ multiple VLAN identifiers.";
+ }
+ }
+ }
+ uses service-qos;
+ container sdp-peering {
+ description
+ "Describes SDP peering attributes.";
+ leaf peer-sap-id {
+ type string;
+ description
+ "Indicates a reference to the remote endpoints
+ of an Attachment Circuit. This information can
+ be used for correlation purposes, such as
+ identifying a Service Attachment Point (SAP)
+ of a provider equipment when requesting a
+ service with CE based SDP attributes.";
+ reference
+ "RFC 9408: A YANG Network Data Model for
+ Service Attachment Points (SAPs)";
+ }
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 53]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ container protocols {
+ description
+ "Serves as an augmentation target.
+ Protocols can be augmented into this container,
+ e.g., BGP or static routing.";
+ }
+ }
+ uses ac-common:service-status;
+ }
+ }
+ uses ac-common:service-status;
+ container sdp-monitoring {
+ config false;
+ description
+ "Container for SDP monitoring metrics.";
+ leaf incoming-bw-value {
+ type yang:gauge64;
+ units "bps";
+ description
+ "Indicates the absolute value of the incoming
+ bandwidth at an SDP from the customer network or
+ from another provider's network.";
+ }
+ leaf incoming-bw-percent {
+ type percentage;
+ units "percent";
+ description
+ "Indicates a percentage of the incoming bandwidth
+ at an SDP from the customer network or
+ from another provider's network.";
+ }
+ leaf outgoing-bw-value {
+ type yang:gauge64;
+ units "bps";
+ description
+ "Indicates the absolute value of the outgoing
+ bandwidth at an SDP towards the customer network or
+ towards another provider's network.";
+ }
+ leaf outgoing-bw-percent {
+ type percentage;
+ units "percent";
+ description
+ "Indicates a percentage of the outgoing bandwidth
+ at an SDP towards the customer network or towards
+ another provider's network.";
+ }
+ }
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 54]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ }
+ }
+ container connection-groups {
+ description
+ "Contains Connection Groups.";
+ list connection-group {
+ key "id";
+ description
+ "List of Connection Groups.";
+ leaf id {
+ type string;
+ description
+ "The Connection Group identifier.";
+ }
+ leaf connectivity-type {
+ type identityref {
+ base vpn-common:vpn-topology;
+ }
+ default "vpn-common:any-to-any";
+ description
+ "Connection Group connectivity type.";
+ }
+ uses service-slo-sle-policy;
+ /* Per Connection Group SLO/SLE policy
+ * overrides the per Slice SLO/SLE policy.
+ */
+ uses service-slo-sle-policy-override;
+ list connectivity-construct {
+ key "id";
+ description
+ "List of Connectivity Constructs.";
+ leaf id {
+ type string;
+ description
+ "The Connectivity Construct identifier.";
+ }
+ choice type {
+ default "p2p";
+ description
+ "Choice for Connectivity Construct type.";
+ case p2p {
+ description
+ "P2P Connectivity Construct.";
+ leaf p2p-sender-sdp {
+ type leafref {
+ path "../../../../sdps/sdp/id";
+ }
+ description
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 55]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "Reference to a sender SDP.";
+ }
+ leaf p2p-receiver-sdp {
+ type leafref {
+ path "../../../../sdps/sdp/id";
+ }
+ description
+ "Reference to a receiver SDP.";
+ }
+ }
+ case p2mp {
+ description
+ "P2MP Connectivity Construct.";
+ leaf p2mp-sender-sdp {
+ type leafref {
+ path "../../../../sdps/sdp/id";
+ }
+ description
+ "Reference to a sender SDP.";
+ }
+ leaf-list p2mp-receiver-sdp {
+ type leafref {
+ path "../../../../sdps/sdp/id";
+ }
+ description
+ "Reference to a receiver SDP.";
+ }
+ }
+ case a2a {
+ description
+ "A2A Connectivity Construct.";
+ list a2a-sdp {
+ key "sdp-id";
+ description
+ "List of included A2A SDPs.";
+ leaf sdp-id {
+ type leafref {
+ path "../../../../../sdps/sdp/id";
+ }
+ description
+ "Reference to an SDP.";
+ }
+ uses service-slo-sle-policy;
+ }
+ }
+ }
+ uses service-slo-sle-policy;
+ /* Per Connectivity Construct SLO/SLE policy
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 56]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ * overrides the per slice SLO/SLE policy.
+ */
+ uses service-slo-sle-policy-override;
+ uses ac-common:service-status;
+ container connectivity-construct-monitoring {
+ config false;
+ description
+ "SLO status per Connectivity Construct.";
+ uses connectivity-construct-monitoring-metrics;
+ }
+ }
+ container connection-group-monitoring {
+ config false;
+ description
+ "SLO status per Connection Group.";
+ uses connectivity-construct-monitoring-metrics;
+ }
+ }
+ }
+ container custom-topology {
+ description
+ "Serves as an augmentation target.
+ Container for custom topology, which is indicated by the
+ referenced topology predefined, e.g., an abstract RFC8345
+ topology.";
+ uses nw:network-ref;
+ }
+ }
+ }
+ }
+
+
+ Figure 17: Network Slice Service YANG Module
+
+
+7. Security Considerations
+
+ This section is modeled after the template described in Section 3.7
+ of [I-D.ietf-netmod-rfc8407bis].
+
+ The "ietf-network-slice-service" YANG module defines a data model
+ that is designed to be accessed via YANG-based management protocols,
+ such as NETCONF [RFC6241] or RESTCONF [RFC8040]. These protocols
+ have to use a secure transport layer (e.g., SSH [RFC4252], TLS
+ [RFC8446], and QUIC [RFC9000]). The YANG-based management protocols
+ also have to use mutual authentication.
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 57]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ Servers MUST verify that requesting clients are entitled to access
+ and manipulate a given Network Slice Service. The Network
+ Configuration Access Control Model (NACM) [RFC8341] provides the
+ means to restrict access for particular NETCONF or RESTCONF users to
+ a preconfigured subset of all available NETCONF or RESTCONF protocol
+ operations and content.
+
+ There are a number of data nodes defined in these YANG modules that
+ are writable/creatable/deletable (i.e., config true, which is the
+ default). All writable data nodes are likely to be reasonably
+ sensitive or vulnerable in some network environments. Write
+ operations (e.g., edit-config) and delete operations to these data
+ nodes without proper protection or authentication can have a negative
+ effect on network operations. Specifically, the following subtrees
+ and data nodes have particular sensitivities/vulnerabilities in the
+ "ietf-network-slice-service" module:
+
+ * /ietf-network-slice-service/network-slice-services/slo-sle-
+ templates
+
+ This subtree specifies the Network Slice Service SLO templates and
+ SLE templates. Modifying the configuration in the subtree will
+ change the related Network Slice Service configuration in the future.
+ By making such modifications, a malicious attacker may degrade the
+ Slice Service functions configured at a certain time in the future.
+
+ * /ietf-network-slice-service/network-slice-services/slice-service
+
+ The entries in the list above include the whole network
+ configurations corresponding with the Network Slice Service which the
+ higher management system requests, and indirectly create or modify
+ the PE or P device configurations. Unexpected changes to these
+ entries could lead to service disruption and/or network misbehavior.
+
+ Some of the readable data nodes in these YANG modules may be
+ considered sensitive or vulnerable in some network environments. It
+ is thus important to control read access (e.g., via get, get-config,
+ or notification) to these data nodes. Specifically, the following
+ subtrees and data nodes have particular sensitivity/vulnerability in
+ the "ietf-network-slice-service" module:
+
+ * /ietf-network-slice-service/network-slice-services/slo-sle-
+ templates
+
+ Unauthorized access to the subtree may disclose the SLO and SLE
+ templates of the Network Slice Service.
+
+ * /ietf-network-slice-service/network-slice-services/slice-service
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 58]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ Unauthorized access to the subtree may disclose the operation status
+ information of the Network Slice Service.
+
+ * /ietf-network-slice-service/network-slice-services/slice-service/
+ service-tags
+
+ Unauthorized access to the subtree may disclose privacy data such as
+ customer names of the Network Slice Service.
+
+8. IANA Considerations
+
+ This document requests to register the following URI in the IETF XML
+ registry [RFC3688]:
+
+ URI: urn:ietf:params:xml:ns:yang:ietf-network-slice-service
+ Registrant Contact: The IESG.
+ XML: N/A, the requested URI is an XML namespace.
+
+ This document requests to register the following YANG module in the
+ YANG Module Names registry [RFC6020].
+
+ Name: ietf-network-slice-service
+ Namespace: urn:ietf:params:xml:ns:yang:ietf-network-slice-service
+ Prefix: ietf-nss
+ Maintained by IANA? N
+ Reference: RFC AAAA
+
+9. Acknowledgments
+
+ The authors wish to thank Mohamed Boucadair, Kenichi Ogaki, Sergio
+ Belotti, Qin Wu, Yao Zhao, Eric Grey, Daniele Ceccarelli, Ryan
+ Hoffman, Adrian Farrel, Aihua Guo, Italo Busi, and many others for
+ their helpful comments and suggestions.
+
+ Thanks to Ladislav Lhotka for the YANG Doctors review.
+
+ Thanks to Alvaro Retana and Susan Hares for the rtgdir reviews, Per
+ Andersson for the opsdir review, Mike Ounsworth for the secdir
+ review, Kyle Rose for the tsvdir review, and Ines Robles for the
+ genart review.
+
+ Thanks to Mahesh Jethanandani, Gorry Fairhurst, Eric Vyncke, Ketan
+ Talaulikar, Erik Kline, Mike Bishop, Deb Cooley, and Paul Wouters for
+ the IESG review.
+
+
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 59]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+10. Contributors
+
+ The following authors contributed significantly to this document:
+
+ Luis M. Contreras
+ Telefonica
+ Spain
+ Email: luismiguel.contrerasmurillo@telefonica.com
+
+ Liuyan Han
+ China Mobile
+ Email: hanliuyan@chinamobile.com
+
+11. References
+
+11.1. Normative References
+
+ [I-D.ietf-opsawg-teas-attachment-circuit]
+ Boucadair, M., Roberts, R., de Dios, O. G., Barguil, S.,
+ and B. Wu, "YANG Data Models for Bearers and 'Attachment
+ Circuits'-as-a-Service (ACaaS)", Work in Progress,
+ Internet-Draft, draft-ietf-opsawg-teas-attachment-circuit-
+ 20, 23 January 2025,
+ .
+
+ [I-D.ietf-opsawg-teas-common-ac]
+ Boucadair, M., Roberts, R., de Dios, O. G., Barguil, S.,
+ and B. Wu, "A Common YANG Data Model for Attachment
+ Circuits", Work in Progress, Internet-Draft, draft-ietf-
+ opsawg-teas-common-ac-15, 23 January 2025,
+ .
+
+ [I-D.ietf-teas-rfc8776-update]
+ Busi, I., Guo, A., Liu, X., Saad, T., and I. Bryskin,
+ "Common YANG Data Types for Traffic Engineering", Work in
+ Progress, Internet-Draft, draft-ietf-teas-rfc8776-update-
+ 17, 21 February 2025,
+ .
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119,
+ DOI 10.17487/RFC2119, March 1997,
+ .
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 60]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ [RFC2681] Almes, G., Kalidindi, S., and M. Zekauskas, "A Round-trip
+ Delay Metric for IPPM", RFC 2681, DOI 10.17487/RFC2681,
+ September 1999, .
+
+ [RFC3393] Demichelis, C. and P. Chimento, "IP Packet Delay Variation
+ Metric for IP Performance Metrics (IPPM)", RFC 3393,
+ DOI 10.17487/RFC3393, November 2002,
+ .
+
+ [RFC3688] Mealling, M., "The IETF XML Registry", BCP 81, RFC 3688,
+ DOI 10.17487/RFC3688, January 2004,
+ .
+
+ [RFC6020] Bjorklund, M., Ed., "YANG - A Data Modeling Language for
+ the Network Configuration Protocol (NETCONF)", RFC 6020,
+ DOI 10.17487/RFC6020, October 2010,
+ .
+
+ [RFC6991] Schoenwaelder, J., Ed., "Common YANG Data Types",
+ RFC 6991, DOI 10.17487/RFC6991, July 2013,
+ .
+
+ [RFC7679] Almes, G., Kalidindi, S., Zekauskas, M., and A. Morton,
+ Ed., "A One-Way Delay Metric for IP Performance Metrics
+ (IPPM)", STD 81, RFC 7679, DOI 10.17487/RFC7679, January
+ 2016, .
+
+ [RFC7680] Almes, G., Kalidindi, S., Zekauskas, M., and A. Morton,
+ Ed., "A One-Way Loss Metric for IP Performance Metrics
+ (IPPM)", STD 82, RFC 7680, DOI 10.17487/RFC7680, January
+ 2016, .
+
+ [RFC7950] Bjorklund, M., Ed., "The YANG 1.1 Data Modeling Language",
+ RFC 7950, DOI 10.17487/RFC7950, August 2016,
+ .
+
+ [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
+ 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
+ May 2017, .
+
+ [RFC8294] Liu, X., Qu, Y., Lindem, A., Hopps, C., and L. Berger,
+ "Common YANG Data Types for the Routing Area", RFC 8294,
+ DOI 10.17487/RFC8294, December 2017,
+ .
+
+ [RFC8340] Bjorklund, M. and L. Berger, Ed., "YANG Tree Diagrams",
+ BCP 215, RFC 8340, DOI 10.17487/RFC8340, March 2018,
+ .
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 61]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ [RFC8341] Bierman, A. and M. Bjorklund, "Network Configuration
+ Access Control Model", STD 91, RFC 8341,
+ DOI 10.17487/RFC8341, March 2018,
+ .
+
+ [RFC8342] Bjorklund, M., Schoenwaelder, J., Shafer, P., Watsen, K.,
+ and R. Wilton, "Network Management Datastore Architecture
+ (NMDA)", RFC 8342, DOI 10.17487/RFC8342, March 2018,
+ .
+
+ [RFC8345] Clemm, A., Medved, J., Varga, R., Bahadur, N.,
+ Ananthakrishnan, H., and X. Liu, "A YANG Data Model for
+ Network Topologies", RFC 8345, DOI 10.17487/RFC8345, March
+ 2018, .
+
+ [RFC8519] Jethanandani, M., Agarwal, S., Huang, L., and D. Blair,
+ "YANG Data Model for Network Access Control Lists (ACLs)",
+ RFC 8519, DOI 10.17487/RFC8519, March 2019,
+ .
+
+ [RFC8639] Voit, E., Clemm, A., Gonzalez Prieto, A., Nilsen-Nygaard,
+ E., and A. Tripathy, "Subscription to YANG Notifications",
+ RFC 8639, DOI 10.17487/RFC8639, September 2019,
+ .
+
+ [RFC8641] Clemm, A. and E. Voit, "Subscription to YANG Notifications
+ for Datastore Updates", RFC 8641, DOI 10.17487/RFC8641,
+ September 2019, .
+
+ [RFC9179] Hopps, C., "A YANG Grouping for Geographic Locations",
+ RFC 9179, DOI 10.17487/RFC9179, February 2022,
+ .
+
+ [RFC9181] Barguil, S., Gonzalez de Dios, O., Ed., Boucadair, M.,
+ Ed., and Q. Wu, "A Common YANG Data Model for Layer 2 and
+ Layer 3 VPNs", RFC 9181, DOI 10.17487/RFC9181, February
+ 2022, .
+
+ [RFC9408] Boucadair, M., Ed., Gonzalez de Dios, O., Barguil, S., Wu,
+ Q., and V. Lopez, "A YANG Network Data Model for Service
+ Attachment Points (SAPs)", RFC 9408, DOI 10.17487/RFC9408,
+ June 2023, .
+
+ [RFC9543] Farrel, A., Ed., Drake, J., Ed., Rokui, R., Homma, S.,
+ Makhijani, K., Contreras, L., and J. Tantsura, "A
+ Framework for Network Slices in Networks Built from IETF
+ Technologies", RFC 9543, DOI 10.17487/RFC9543, March 2024,
+ .
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 62]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+11.2. Informative References
+
+ [I-D.ietf-netmod-rfc8407bis]
+ Bierman, A., Boucadair, M., and Q. Wu, "Guidelines for
+ Authors and Reviewers of Documents Containing YANG Data
+ Models", Work in Progress, Internet-Draft, draft-ietf-
+ netmod-rfc8407bis-24, 18 April 2025,
+ .
+
+ [I-D.ietf-teas-ietf-network-slice-use-cases]
+ Contreras, L. M., Homma, S., Ordonez-Lucena, J. A.,
+ Tantsura, J., and H. Nishihara, "IETF Network Slice Use
+ Cases and Attributes for the Slice Service Interface of
+ IETF Network Slice Controllers", Work in Progress,
+ Internet-Draft, draft-ietf-teas-ietf-network-slice-use-
+ cases-01, 24 October 2022,
+ .
+
+ [IEEE802.1ad]
+ "Amendment to IEEE 802.1Q-2005. IEEE Standard for Local
+ and Metropolitan Area Networks - Virtual Bridged Local
+ Area Networks Revision-Amendment 4: Provider Bridges",
+ IEEE Std 802.1ad, 2005.
+
+ [IEEE802.1Q]
+ "IEEE Standard for Local and metropolitan area networks--
+ Bridges and Bridged Networks", IEEE Std 802.1Q, 2022.
+
+ [RFC4252] Ylonen, T. and C. Lonvick, Ed., "The Secure Shell (SSH)
+ Authentication Protocol", RFC 4252, DOI 10.17487/RFC4252,
+ January 2006, .
+
+ [RFC5481] Morton, A. and B. Claise, "Packet Delay Variation
+ Applicability Statement", RFC 5481, DOI 10.17487/RFC5481,
+ March 2009, .
+
+ [RFC6241] Enns, R., Ed., Bjorklund, M., Ed., Schoenwaelder, J., Ed.,
+ and A. Bierman, Ed., "Network Configuration Protocol
+ (NETCONF)", RFC 6241, DOI 10.17487/RFC6241, June 2011,
+ .
+
+ [RFC8040] Bierman, A., Bjorklund, M., and K. Watsen, "RESTCONF
+ Protocol", RFC 8040, DOI 10.17487/RFC8040, January 2017,
+ .
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 63]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ [RFC8309] Wu, Q., Liu, W., and A. Farrel, "Service Models
+ Explained", RFC 8309, DOI 10.17487/RFC8309, January 2018,
+ .
+
+ [RFC8446] Rescorla, E., "The Transport Layer Security (TLS) Protocol
+ Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018,
+ .
+
+ [RFC8650] Voit, E., Rahman, R., Nilsen-Nygaard, E., Clemm, A., and
+ A. Bierman, "Dynamic Subscription to YANG Events and
+ Datastores over RESTCONF", RFC 8650, DOI 10.17487/RFC8650,
+ November 2019, .
+
+ [RFC8792] Watsen, K., Auerswald, E., Farrel, A., and Q. Wu,
+ "Handling Long Lines in Content of Internet-Drafts and
+ RFCs", RFC 8792, DOI 10.17487/RFC8792, June 2020,
+ .
+
+ [RFC8795] Liu, X., Bryskin, I., Beeram, V., Saad, T., Shah, H., and
+ O. Gonzalez de Dios, "YANG Data Model for Traffic
+ Engineering (TE) Topologies", RFC 8795,
+ DOI 10.17487/RFC8795, August 2020,
+ .
+
+ [RFC9000] Iyengar, J., Ed. and M. Thomson, Ed., "QUIC: A UDP-Based
+ Multiplexed and Secure Transport", RFC 9000,
+ DOI 10.17487/RFC9000, May 2021,
+ .
+
+ [RFC9731] Lee, Y., Ed., Dhody, D., Ed., Ceccarelli, D., Bryskin, I.,
+ and B. Yoon, "A YANG Data Model for Virtual Network (VN)
+ Operations", RFC 9731, DOI 10.17487/RFC9731, March 2025,
+ .
+
+11.3. References
+
+Appendix A. Augmentation Considerations
+
+ The NSSM defines the minimum attributes of Slice Services. In some
+ scenarios, further extension, e.g., the definition of AC technology
+ specific attributes and the "isolation" SLE characteristics are
+ required.
+
+
+
+
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 64]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ For AC technology specific attributes, if the customer and provider
+ need to agree, through configuration, on the technology parameter
+ values, such as the protocol types and protocol parameters between
+ the PE and the CE. The following shows an example where BGP and
+ static routing are augmented to the Network Slice Service model. The
+ protocol types and definitions can reference
+ [I-D.ietf-opsawg-teas-common-ac].
+
+ module: ietf-network-slice-service-proto-ex
+ augment /ietf-nss:network-slice-services/ietf-nss:slice-service
+ /ietf-nss:sdps/ietf-nss:sdp/ietf-nss:sdp-peering
+ /ietf-nss:protocols:
+ +--rw bgp
+ | +--rw name? string
+ | +--ro local-as? inet:as-number
+ | +--rw peer-as? inet:as-number
+ | +--rw address-family? identityref
+ +--rw static-routing-ipv4
+ | +--rw lan? inet:ipv4-prefix
+ | +--rw lan-tag? string
+ | +--rw next-hop? union
+ | +--rw metric? uint32
+ +--rw static-routing-ipv6
+ +--rw lan? inet:ipv6-prefix
+ +--rw lan-tag? string
+ +--rw next-hop? union
+ +--rw metric? uint32
+
+ Figure 18: Example YANG Tree Augmenting SDP Peering Protocols
+
+ In some scenarios, for example, when multiple Slice Services share
+ one or more ACs, independent AC services, defined in
+ [I-D.ietf-opsawg-teas-attachment-circuit], can be used.
+
+ For "isolation" SLE characteristics, the following identities can be
+ defined.
+
+ identity service-interference-isolation-dedicated {
+ base service-isolation-type;
+ description
+ "Specify the requirement that the Slice Service is not impacted
+ by the existence of other customers or services in the same
+ network, which may be provided by the service provider using
+ dedicated network resources, similar to a dedicated
+ private network.";
+ }
+
+ Figure 19: Example "isolation" Identity Augmentation
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 65]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+Appendix B. Examples of Network Slice Services
+
+B.1. Example-1: Two A2A Slice Services with Different Match Approaches
+
+ Figure 20 shows an example of two Network Slice Service instances
+ where the SDPs are the customer-facing ports on the PE:
+
+ * Network Slice 1 on SDP1, SDP11a, and SDP4, with an A2A
+ connectivity type. This is an L3 Slice Service that uses the
+ uniform low latency "slo-sle-template" policy between all SDPs.
+ These SDPs will also have AC eBGP peering sessions with unmanaged
+ CE elements (not shown) using an AC augmentation model such as the
+ one shown above.
+
+ * Network Slice 2 on SDP2, SDP11b, with A2A connectivity type. This
+ is an L3 Slice Service that uses the uniform high bandwidth "slo-
+ sle-template" policy between all SDPs.
+
+ Slice 1 uses the explicit match approach for mapping SDP traffic to a
+ "connectivity-construct", while slice 2 uses the implicit approach.
+ Both approaches are supported. The "slo-sle-templates" templates are
+ known to the customer.
+
+ Note: These two slices both use service-tags of "L3". This "service-
+ tag" is operator defined and has no specific meaning in the YANG
+ model other than to give a hint to the NSC on the service expectation
+ being L3 forwarding. This tag may be omitted in other examples, as
+ its usage depends entirely on the needs of the operator and the NSC.
+
+ +--------+ 192.0.2.1/26
+ |CE1 o------/ VLAN100
+ +--------+ | SDP1 +------+
+ +--------+ +------o| PE A+-------------+
+ |CE2 o-------/-----o| | |
+ +--------+ SDP2 +---+--+ |
+ 198.51.100.1/26| | 192.0.2.129/26
+ VLAN200 | +---+--+ VLAN100
+ | | | SDP4 +--------+
+ | |PE C o-----/-----o CE21 |
+ +--------+ 192.0.2.65/26 | +---+--+ +--------+
+ | o------/ VLAN101 | |
+ | | | SDP11a+---+---+ |
+ |CE11 | +------o|PE B +------------+
+ | o-------/-----o| |
+ +--------+ SDP11b+------ +
+ 198.51.100.65/26
+ VLAN201
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 66]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ Figure 20: Example of Two A2A Slice Services
+
+ Figure 21 shows an example YANG JSON data for the body of the Network
+ Slice Service instances request.
+
+ ============== NOTE: '\' line wrapping per RFC 8792 ===============
+
+ {
+ "ietf-network-slice-service:network-slice-services": {
+ "slo-sle-templates": {
+ "slo-sle-template": [
+ {
+ "id": "high-BW-template",
+ "description": "take the highest BW forwarding path"
+ },
+ {
+ "id": "low-latency-template",
+ "description": \
+ "lowest possible latency forwarding behavior"
+ }
+ ]
+ },
+ "slice-service": [
+ {
+ "id": "slice1",
+ "description": "example slice1",
+ "service-tags": {
+ "tag-type": [
+ {
+ "tag-type": "service",
+ "tag-type-value": [
+ "L3"
+ ]
+ }
+ ]
+ },
+ "slo-sle-template": "low-latency-template",
+ "status": {
+ "admin-status": {
+ "status": "ietf-vpn-common:admin-up"
+ }
+ },
+ "sdps": {
+ "sdp": [
+ {
+ "id": "1",
+ "node-id": "PE-A",
+ "service-match-criteria": {
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 67]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix1",
+ "target-connectivity-construct-id": "1"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac1",
+ "description": "AC1 connected to device 1",
+ "ac-node-id": "PE-A",
+ "ac-tp-id": "GigabitEthernet5/0/0/0.100",
+ "ac-ipv4-address": "192.0.2.1",
+ "ac-ipv4-prefix-length": 26,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "100"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "3a",
+ "node-id": "PE-B",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 68]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "target-connection-group-id": "matrix1",
+ "target-connectivity-construct-id": "1"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac3a",
+ "description": "AC3a connected to device 3",
+ "ac-node-id": "PE-B",
+ "ac-tp-id": "GigabitEthernet8/0/0/4.101",
+ "ac-ipv4-address": "192.0.2.65",
+ "ac-ipv4-prefix-length": 26,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "101"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "4",
+ "node-id": "PE-C",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix1",
+ "target-connectivity-construct-id": "1"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 69]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "id": "ac4",
+ "description": "AC4 connected to device 4",
+ "ac-node-id": "PE-C",
+ "ac-tp-id": "GigabitEthernet4/0/0/3.100",
+ "ac-ipv4-address": "192.0.2.129",
+ "ac-ipv4-prefix-length": 26,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "100"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "connection-groups": {
+ "connection-group": [
+ {
+ "id": "matrix1",
+ "connectivity-type": "ietf-vpn-common:any-to-any",
+ "connectivity-construct": [
+ {
+ "id": "1",
+ "a2a-sdp": [
+ {
+ "sdp-id": "1"
+ },
+ {
+ "sdp-id": "3a"
+ },
+ {
+ "sdp-id": "4"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 70]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "id": "slice2",
+ "description": "example slice2",
+ "service-tags": {
+ "tag-type": [
+ {
+ "tag-type": "service",
+ "tag-type-value": [
+ "L3"
+ ]
+ }
+ ]
+ },
+ "slo-sle-template": "high-BW-template",
+ "status": {
+ "admin-status": {
+ "status": "ietf-vpn-common:admin-up"
+ }
+ },
+ "sdps": {
+ "sdp": [
+ {
+ "id": "2",
+ "node-id": "PE-A",
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac2",
+ "description": "AC2 connected to device 2",
+ "ac-node-id": "PE-A",
+ "ac-tp-id": "GigabitEthernet7/0/0/3.200",
+ "ac-ipv4-address": "198.51.100.1",
+ "ac-ipv4-prefix-length": 26,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "100"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "3b",
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 71]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "node-id": "PE-B",
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac3b",
+ "description": "AC3b connected to device 3",
+ "ac-node-id": "PE-B",
+ "ac-tp-id": "GigabitEthernet8/0/0/4.201",
+ "ac-ipv4-address": "198.51.100.65",
+ "ac-ipv4-prefix-length": 26,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "201"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "connection-groups": {
+ "connection-group": [
+ {
+ "id": "matrix2",
+ "connectivity-type": "ietf-vpn-common:any-to-any",
+ "connectivity-construct": [
+ {
+ "id": "1",
+ "a2a-sdp": [
+ {
+ "sdp-id": "2"
+ },
+ {
+ "sdp-id": "3b"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 72]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ ]
+ }
+ }
+
+
+ Figure 21: Example of a Message Body to Create Two A2A Slice Services
+
+B.2. Example-2: Two P2P Slice Services with Different Match Approaches
+
+ Figure 22 shows an example of two Network Slice Service instances
+ where the SDPs are the customer-facing ports on the PE:
+
+ * Network Slice 3 on SDP5 and SDP7a with P2P connectivity type.
+ This is an L2 Slice Service that uses the uniform low-latency
+ "slo-sle-template" policies between the SDPs. A connectivity-
+ group level slo-policy has been applied with a delay-based metric
+ bound of 10ms which will apply to both connectivity-constructs.
+
+ * Network Slice 4 on SDP6 and SDP7b, with P2P connectivity type.
+ This is an L2 Slice Service that uses the high bandwidth "slo-sle-
+ template" policies between the SDPs. Traffic from SDP6 and SDP7b
+ is requesting a bandwidth of 1000Mbps, while in the reverse
+ direction from SDP7b to SDP6, 5000Mbps is being requested.
+
+ Slice 3 uses the explicit match approach for mapping SDP traffic to a
+ "connectivity-group", while slice 2 uses the implicit approach. Both
+ approaches are supported.
+
+ Note: These two slices both use service-tags of "L2". This "service-
+ tag" is operator defined and simply indicateas L2 forwarding
+ expectation to the NSC. This tag may be omitted in other examples,
+ as its usage depends on the needs of the operator and the NSC.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 73]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ +--------+
+ | CE5 o------/ VLAN100
+ +--------+ | SDP5 +------+
+ +--------+ +------o| PE A +---------------+
+ | CE6 o-------/-----o| | |
+ +--------+ SDP6 +---+--+ |
+ VLAN200 | |
+ | +---+--+
+ | | |
+ | | PE C o
+ +--------+ | +---+--+
+ | o------/ VLAN101 | |
+ | | | SDP7a +---+--+ |
+ | CE7 | +------o| PE B +---------------+
+ | o-------/-----o| |
+ +--------+ SDP7b +------+
+ VLAN201
+
+ Figure 22: Example of Two P2P Slice Services
+
+ Figure 23 shows an example YANG JSON data for the body of the Network
+ Slice Service instances request.
+
+ ============== NOTE: '\' line wrapping per RFC 8792 ===============
+
+ {
+ "ietf-network-slice-service:network-slice-services": {
+ "slo-sle-templates": {
+ "slo-sle-template": [
+ {
+ "id": "high-BW-template",
+ "description": "take the highest BW forwarding path"
+ },
+ {
+ "id": "low-latency-template",
+ "description": \
+ "lowest possible latency forwarding behavior"
+ }
+ ]
+ },
+ "slice-service": [
+ {
+ "id": "slice3",
+ "description": "example slice3",
+ "service-tags": {
+ "tag-type": [
+ {
+ "tag-type": "service",
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 74]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "tag-type-value": [
+ "L2"
+ ]
+ }
+ ]
+ },
+ "slo-sle-template": "low-latency-template",
+ "status": {
+ "admin-status": {
+ "status": "ietf-vpn-common:admin-up"
+ }
+ },
+ "sdps": {
+ "sdp": [
+ {
+ "id": "5",
+ "node-id": "PE-A",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix3"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac5",
+ "description": "AC5 connected to device 5",
+ "ac-node-id": "PE-A",
+ "ac-tp-id": "GigabitEthernet5/0/0/1",
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "100"
+ ]
+ }
+ ]
+ }
+ }
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 75]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ ]
+ }
+ },
+ {
+ "id": "7a",
+ "node-id": "PE-B",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix3"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac7a",
+ "description": "AC7a connected to device 7",
+ "ac-node-id": "PE-B",
+ "ac-tp-id": "GigabitEthernet8/0/0/5",
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "200"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "connection-groups": {
+ "connection-group": [
+ {
+ "id": "matrix3",
+ "connectivity-type": "point-to-point",
+ "service-slo-sle-policy": {
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 76]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "slo-policy": {
+ "metric-bound": [
+ {
+ "metric-type": "one-way-delay-maximum",
+ "metric-unit": "milliseconds",
+ "bound": "10"
+ }
+ ]
+ }
+ },
+ "connectivity-construct": [
+ {
+ "id": "1",
+ "p2p-sender-sdp": "5",
+ "p2p-receiver-sdp": "7a"
+ },
+ {
+ "id": "2",
+ "p2p-sender-sdp": "7a",
+ "p2p-receiver-sdp": "5"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "id": "slice4",
+ "description": "example slice4",
+ "service-tags": {
+ "tag-type": [
+ {
+ "tag-type": "service",
+ "tag-type-value": [
+ "L2"
+ ]
+ }
+ ]
+ },
+ "slo-sle-template": "high-BW-template",
+ "status": {
+ "admin-status": {
+ "status": "ietf-vpn-common:admin-up"
+ }
+ },
+ "sdps": {
+ "sdp": [
+ {
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 77]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "id": "6",
+ "node-id": "PE-A",
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac6",
+ "description": "AC6 connected to device 6",
+ "ac-node-id": "PE-A",
+ "ac-tp-id": "GigabitEthernet7/0/0/4",
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "101"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "7b",
+ "node-id": "PE-B",
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac7b",
+ "description": "AC7b connected to device 7",
+ "ac-node-id": "PE-B",
+ "ac-tp-id": "GigabitEthernet8/0/0/5",
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "201"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 78]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ },
+ "connection-groups": {
+ "connection-group": [
+ {
+ "id": "matrix4",
+ "connectivity-type": "point-to-point",
+ "connectivity-construct": [
+ {
+ "id": "1",
+ "p2p-sender-sdp": "6",
+ "p2p-receiver-sdp": "7b",
+ "service-slo-sle-policy": {
+ "slo-policy": {
+ "metric-bound": [
+ {
+ "metric-type": "one-way-bandwidth",
+ "metric-unit": "Mbps",
+ "bound": "1000"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "id": "2",
+ "p2p-sender-sdp": "7b",
+ "p2p-receiver-sdp": "6",
+ "service-slo-sle-policy": {
+ "slo-policy": {
+ "metric-bound": [
+ {
+ "metric-type": "one-way-bandwidth",
+ "metric-unit": "Mbps",
+ "bound": "5000"
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 79]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ Figure 23: Example of a Message Body to Create Two P2P Slice Services
+
+ The example shown in Figure 24 illustrates how a customer might
+ subscribe to the monitoring information of "slice3" with the
+ "establish-subscription" RPC [RFC8650]. The customer is interested
+ in the operational and performance status of SDPs and Connectivity
+ Constructs.
+
+ ============== NOTE: '\' line wrapping per RFC 8792 ===============
+
+ POST /restconf/operations/ietf-subscribed-notifications:establish-\
+ subscription
+ Host: example.com
+ Content-Type: application/yang-data+json
+
+ {
+ "ietf-subscribed-notifications:input": {
+ "ietf-yang-push:datastore": "ietf-datastores:running",
+ "ietf-yang-push:datastore-subtree-filter": {
+ "ietf-network-slice-service:network-slice-services": {
+ "slice-service": [
+ {
+ "id": "slice3",
+ "sdps": {
+ "sdp": [
+ {
+ "id": "5",
+ "status": {
+ "oper-status": {
+ "status": {}
+ }
+ },
+ "sdp-monitoring": {
+ "incoming-bw-value": {},
+ "outgoing-bw-value": {}
+ }
+ },
+ {
+ "id": "7a",
+ "status": {
+ "oper-status": {
+ "status": {}
+ }
+ },
+ "sdp-monitoring": {
+ "incoming-bw-value": {},
+ "outgoing-bw-value": {}
+ }
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 80]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ }
+ ]
+ },
+ "connection-groups": {
+ "connection-group": [
+ {
+ "id": "matrix3",
+ "connectivity-type": "point-to-point",
+ "connectivity-construct": [
+ {
+ "id": "1",
+ "p2p-sender-sdp": "5",
+ "p2p-receiver-sdp": "7a",
+ "status": {
+ "oper-status": {
+ "status": "{}"
+ }
+ },
+ "connectivity-construct-monitoring": {
+ "one-way-min-delay": {},
+ "one-way-max-delay": {}
+ }
+ },
+ {
+ "id": "2",
+ "p2p-sender-sdp": "7a",
+ "p2p-receiver-sdp": "5",
+ "status": {
+ "oper-status": {
+ "status": {}
+ }
+ },
+ "connectivity-construct-monitoring": {
+ "one-way-min-delay": {},
+ "one-way-max-delay": {}
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ "ietf-yang-push:periodic": {
+ "period": "500"
+ }
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 81]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ }
+ }
+
+ Figure 24: Example of a Message Body to Subscribe Monitoring
+ Information of the Slice Service
+
+ The example Figure 25 shows a snapshot of YANG JSON data for the body
+ of operational and performance status of the Network Slice Service
+ "slice3".
+
+
+ {
+ "ietf-network-slice-service:network-slice-services": {
+ "slice-service": [
+ {
+ "id": "slice3",
+ "description": "example slice3",
+ "slo-sle-template": "low-latency-template",
+ "status": {
+ "oper-status": {
+ "status": "ietf-vpn-common:op-up"
+ }
+ },
+ "sdps": {
+ "sdp": [
+ {
+ "id": "5",
+ "node-id": "PE-A",
+ "status": {
+ "oper-status": {
+ "status": "ietf-vpn-common:op-up"
+ }
+ },
+ "sdp-monitoring": {
+ "incoming-bw-value": "10000",
+ "outgoing-bw-value": "10000"
+ }
+ },
+ {
+ "id": "7a",
+ "node-id": "PE-B",
+ "status": {
+ "oper-status": {
+ "status": "ietf-vpn-common:op-up"
+ }
+ },
+ "sdp-monitoring": {
+ "incoming-bw-value": "10000",
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 82]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "outgoing-bw-value": "10000"
+ }
+ }
+ ]
+ },
+ "connection-groups": {
+ "connection-group": [
+ {
+ "id": "matrix3",
+ "connectivity-type": "point-to-point",
+ "connectivity-construct": [
+ {
+ "id": "1",
+ "p2p-sender-sdp": "5",
+ "p2p-receiver-sdp": "7a",
+ "status": {
+ "oper-status": {
+ "status": "ietf-vpn-common:op-up"
+ }
+ },
+ "connectivity-construct-monitoring": {
+ "one-way-min-delay": "15",
+ "one-way-max-delay": "20"
+ }
+ },
+ {
+ "id": "2",
+ "p2p-sender-sdp": "7a",
+ "p2p-receiver-sdp": "5",
+ "status": {
+ "oper-status": {
+ "status": "ietf-vpn-common:op-up"
+ }
+ },
+ "connectivity-construct-monitoring": {
+ "one-way-min-delay": "15",
+ "one-way-max-delay": "20"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "id": "slice4",
+ "description": "example slice4",
+ "slo-sle-template": "high-BW-template",
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 83]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "status": {
+ "oper-status": {
+ "status": "ietf-vpn-common:op-up"
+ }
+ },
+ "sdps": {
+ "sdp": [
+ {
+ "id": "6",
+ "node-id": "PE-A",
+ "status": {
+ "oper-status": {
+ "status": "ietf-vpn-common:op-up"
+ }
+ },
+ "sdp-monitoring": {
+ "incoming-bw-value": "10000000",
+ "outgoing-bw-value": "10000000"
+ }
+ },
+ {
+ "id": "7b",
+ "node-id": "PE-B",
+ "status": {
+ "oper-status": {
+ "status": "ietf-vpn-common:op-up"
+ }
+ },
+ "sdp-monitoring": {
+ "incoming-bw-value": "10000000",
+ "outgoing-bw-value": "10000000"
+ }
+ }
+ ]
+ },
+ "connection-groups": {
+ "connection-group": [
+ {
+ "id": "matrix4",
+ "connectivity-type": "point-to-point",
+ "connectivity-construct": [
+ {
+ "id": "1",
+ "p2p-sender-sdp": "6",
+ "p2p-receiver-sdp": "7b",
+ "status": {
+ "oper-status": {
+ "status": "ietf-vpn-common:op-up"
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 84]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ }
+ },
+ "connectivity-construct-monitoring": {
+ "one-way-min-delay": "150",
+ "one-way-max-delay": "200"
+ }
+ },
+ {
+ "id": "2",
+ "p2p-sender-sdp": "7b",
+ "p2p-receiver-sdp": "6",
+ "status": {
+ "oper-status": {
+ "status": "ietf-vpn-common:op-up"
+ }
+ },
+ "connectivity-construct-monitoring": {
+ "one-way-min-delay": "150",
+ "one-way-max-delay": "200"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+
+
+ Figure 25: Example of a Message Body of a Snapshot of Monitoring
+ of the Slice Service
+
+B.3. Example-3: A Hub and Spoke Slice Service with a P2MP Connectivity
+ Construct
+
+ Figure 26 shows an example of one Network Slice Service instance
+ where the SDPs are the customer-facing ports on the PE:
+
+ Network Slice 5 is a hub-spoke slice with SDP14 as the hub and
+ SDP11, SDP12, SDP13a, SDP13b as spokes. This is an L3 Slice
+ Service that uses the uniform low-latency "slo-sle-template"
+ policies between all spokes and the hub SDPs, but using an
+ explicit set of SLO policies with a latency metric of 10ms for hub
+ to spoke traffic.
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 85]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ +--------+ 192.0.2.1/26
+ |Device11o------/ VLAN100
+ +--------+ | SDP11+------+
+ +--------+ +------o| A +-------------+
+ |Device12o-------/-----o| | |
+ +--------+ SDP12+---+--+ |
+ 198.51.100.1/26 | | 192.0.2.129/26
+ VLAN200 | +---+--+ VLAN100
+ | | | SDP14 +--------+
+ | | C o-----/-----oDevice14|
+ +--------+ 192.0.2.65/26 | +---+--+ +--------+
+ | o------/ VLAN101 | |
+ | | | SDP13a+---+--+ |
+ |Device13| +------o| B +-------------+
+ | o-------/-----o| |
+ +--------+ SDP13b+------+
+ 198.51.100.65/26
+ VLAN201
+
+ Figure 26: Example of A Hub and Spoke Slice Service
+
+ Figure 27 shows an example YANG JSON data for the body of the hub-
+ spoke Network Slice Service instances request.
+
+ ============== NOTE: '\' line wrapping per RFC 8792 ===============
+
+ {
+ "ietf-network-slice-service:network-slice-services": {
+ "slo-sle-templates": {
+ "slo-sle-template": [
+ {
+ "id": "high-BW-template",
+ "description": "take the highest BW forwarding path"
+ },
+ {
+ "id": "low-latency-template",
+ "description": \
+ "lowest possible latency forwarding behavior"
+ }
+ ]
+ },
+ "slice-service": [
+ {
+ "id": "slice5",
+ "description": "example slice5",
+ "service-tags": {
+ "tag-type": [
+ {
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 86]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "tag-type": "service",
+ "tag-type-value": [
+ "L3"
+ ]
+ }
+ ]
+ },
+ "service-slo-sle-policy": {
+ "description": "video-policy",
+ "slo-policy": {
+ "metric-bound": [
+ {
+ "metric-type": "one-way-bandwidth",
+ "metric-unit": "Mbps",
+ "bound": "1000"
+ },
+ {
+ "metric-type": "two-way-delay-maximum",
+ "metric-unit": "milliseconds",
+ "bound": "100"
+ }
+ ],
+ "availability": "three-nines",
+ "mtu": 1500
+ }
+ },
+ "status": {
+ "admin-status": {
+ "status": "ietf-vpn-common:admin-up"
+ }
+ },
+ "sdps": {
+ "sdp": [
+ {
+ "id": "11",
+ "node-id": "PE-A",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix5",
+ "connection-group-sdp-role": \
+ "ietf-vpn-common:spoke-role"
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 87]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac11",
+ "description": "AC11 connected to device 11",
+ "ac-node-id": "PE-A",
+ "ac-tp-id": "GigabitEthernet5/0/0/2",
+ "ac-ipv4-address": "192.0.2.1",
+ "ac-ipv4-prefix-length": 26,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "100"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "12",
+ "node-id": "PE-A",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix5",
+ "connection-group-sdp-role": \
+ "ietf-vpn-common:spoke-role"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac12",
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 88]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "description": "AC12 connected to device 12",
+ "ac-node-id": "PE-A",
+ "ac-tp-id": "GigabitEthernet7/0/0/5",
+ "ac-ipv4-address": "198.51.100.1",
+ "ac-ipv4-prefix-length": 26,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "200"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "13a",
+ "node-id": "PE-B",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix5",
+ "connection-group-sdp-role": \
+ "ietf-vpn-common:spoke-role"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac13a",
+ "description": "AC13a connected to device 13",
+ "ac-node-id": "PE-B",
+ "ac-tp-id": "GigabitEthernet8/0/0/6",
+ "ac-ipv4-address": "192.0.2.65",
+ "ac-ipv4-prefix-length": 26,
+ "ac-tags": {
+ "ac-tag": [
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 89]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "101"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "13b",
+ "node-id": "PE-B",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix5",
+ "connection-group-sdp-role": \
+ "ietf-vpn-common:spoke-role"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac13b",
+ "description": "AC3b connected to device 13",
+ "ac-node-id": "PE-B",
+ "ac-tp-id": "GigabitEthernet8/0/0/4",
+ "ac-ipv4-address": "198.51.100.65",
+ "ac-ipv4-prefix-length": 26,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "201"
+ ]
+ }
+ ]
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 90]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "14",
+ "node-id": "PE-C",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix5",
+ "connection-group-sdp-role": \
+ "ietf-vpn-common:hub-role"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac14",
+ "description": "AC14 connected to device 14",
+ "ac-node-id": "PE-C",
+ "ac-tp-id": "GigabitEthernet4/0/0/3",
+ "ac-ipv4-address": "192.0.2.129",
+ "ac-ipv4-prefix-length": 26,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "100"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ },
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 91]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "connection-groups": {
+ "connection-group": [
+ {
+ "id": "matrix5",
+ "connectivity-type": "ietf-vpn-common:hub-spoke",
+ "connectivity-construct": [
+ {
+ "id": "1",
+ "p2mp-sender-sdp": "14",
+ "p2mp-receiver-sdp": [
+ "11",
+ "12",
+ "13a",
+ "13b"
+ ],
+ "service-slo-sle-policy": {
+ "slo-policy": {
+ "metric-bound": [
+ {
+ "metric-type": "one-way-delay-maximum",
+ "metric-unit": "milliseconds",
+ "bound": "10"
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+
+
+ Figure 27: Example of a Message Body to Create A Hub and Spoke
+ Slice Service
+
+B.4. Example-4: An A2A Slice Service with Multiple SLOs and DSCP
+ Matching
+
+ Figure 28 shows an example of a Network slice instance where the SDPs
+ are the customer-facing ports on the PE:
+
+ Network Slice 6 on SDP21, SDP23a, and SDP24, with A2A connectivity
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 92]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ type. This is an L3 Slice Service that uses the uniform
+ "standard" slo-sle-template policies between all SDPs. For
+ traffic matching the DSCP of EF, a slo-sle-template policy of
+ "low-latency" will be used. The slice uses the explicit match
+ approach for mapping SDP traffic to a Connectivity Construct.
+
+ In some use cases, the Slice Service may also need to map traffic
+ based on a combination of the DSCP and IP address, not DSCP only,
+ which is shown in the example of "service-match-criteria"
+ Figure 30.
+
+ +--------+ 192.0.2.1/24
+ | CE21 o------/ VLAN100
+ +--------+ | SDP21+------+
+ +------o| PE A +-------------+
+ | | |
+ +---+--+ |
+ | | 203.0.113.1/24
+ | +---+--+ VLAN100
+ | | | SDP24 +--------+
+ | | PE C o-----/-----o CE24 |
+ +--------+ 198.51.100.1/24 | +---+--+ +--------+
+ | o------/ VLAN101 | |
+ | | | SDP23a+---+--+ |
+ |CE23 | +------o| PE B +-------------+
+ | o | |
+ +--------+ +------+
+
+ Figure 28: Example of An A2A Slice Service with DSCP Matching
+
+ Figure 29 shows an example YANG JSON data for the body of the Network
+ Slice Service instances request.
+
+ ============== NOTE: '\' line wrapping per RFC 8792 ===============
+
+ {
+ "ietf-network-slice-service:network-slice-services": {
+ "slo-sle-templates": {
+ "slo-sle-template": [
+ {
+ "id": "high-BW-template",
+ "description": "take the highest BW forwarding path"
+ },
+ {
+ "id": "low-latency-template",
+ "description": \
+ "lowest possible latency forwarding behavior"
+ },
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 93]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ {
+ "id": "standard-template",
+ "description": "take the standard forwarding path"
+ }
+ ]
+ },
+ "slice-service": [
+ {
+ "id": "slice6",
+ "description": "example slice6",
+ "service-tags": {
+ "tag-type": [
+ {
+ "tag-type": "service",
+ "tag-type-value": [
+ "L3"
+ ]
+ }
+ ]
+ },
+ "slo-sle-template": "standard-template",
+ "status": {
+ "admin-status": {
+ "status": "ietf-vpn-common:admin-up"
+ }
+ },
+ "sdps": {
+ "sdp": [
+ {
+ "id": "21",
+ "node-id": "PE-A",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "dscp",
+ "dscp-value": [
+ 46
+ ]
+ }
+ ],
+ "target-connection-group-id": "matrix6",
+ "target-connectivity-construct-id": "2"
+ },
+ {
+ "index": 2,
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 94]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix6",
+ "target-connectivity-construct-id": "1"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac21",
+ "description": "AC21 connected to device 21",
+ "ac-node-id": "PE-A",
+ "ac-tp-id": "GigabitEthernet5/0/0/0",
+ "ac-ipv4-address": "192.0.2.1",
+ "ac-ipv4-prefix-length": 24,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "100"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "23a",
+ "node-id": "PE-B",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "dscp",
+ "dscp-value": [
+ 46
+ ]
+ }
+ ],
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 95]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "target-connection-group-id": "matrix6",
+ "target-connectivity-construct-id": "2"
+ },
+ {
+ "index": 2,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix6",
+ "target-connectivity-construct-id": "1"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac23a",
+ "description": "AC23a connected to device 23",
+ "ac-node-id": "PE-B",
+ "ac-tp-id": "GigabitEthernet8/0/0/4",
+ "ac-ipv4-address": "198.51.100.1",
+ "ac-ipv4-prefix-length": 24,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "101"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "24",
+ "node-id": "PE-C",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "dscp",
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 96]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "dscp-value": [
+ 46
+ ]
+ }
+ ],
+ "target-connection-group-id": "matrix6",
+ "target-connectivity-construct-id": "2"
+ },
+ {
+ "index": 2,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix6",
+ "target-connectivity-construct-id": "1"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac24",
+ "description": "AC24 connected to device 24",
+ "ac-node-id": "PE-C",
+ "ac-tp-id": "GigabitEthernet4/0/0/3",
+ "ac-ipv4-address": "203.0.113.1",
+ "ac-ipv4-prefix-length": 24,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "100"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "connection-groups": {
+ "connection-group": [
+ {
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 97]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "id": "matrix6",
+ "connectivity-type": "ietf-vpn-common:any-to-any",
+ "connectivity-construct": [
+ {
+ "id": "1",
+ "a2a-sdp": [
+ {
+ "sdp-id": "21"
+ },
+ {
+ "sdp-id": "23a"
+ },
+ {
+ "sdp-id": "24"
+ }
+ ]
+ },
+ {
+ "id": "2",
+ "a2a-sdp": [
+ {
+ "sdp-id": "21"
+ },
+ {
+ "sdp-id": "23a"
+ },
+ {
+ "sdp-id": "24",
+ "slo-sle-template": "low-latency-template"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+
+
+ Figure 29: Example of a Message Body to Create An A2A Slice
+ Service with DSCP Matching
+
+
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 98]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ Figure 30 shows an example of "service-match-criteria" with a
+ combination of both DSCP and IP Address for the Slice Service traffic
+ matching.
+
+ {
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "dscp",
+ "dscp-value": [
+ 46
+ ]
+ },
+ {
+ "type": " destination-ip-prefix",
+ "ip-prefix": [
+ "192.0.2.254"
+ ]
+ }
+ ],
+ "target-connection-group-id": "matrix6",
+ "target-connectivity-construct-id": "2"
+ }
+ ]
+ }
+ }
+
+ Figure 30: An Example of Match Criterion with Combination of DSCP
+ and IP Address Matching
+
+B.5. Example-5: An A2A Network Slice Service with SLO Precedence
+ Policies
+
+ Figure 31 shows an example of a Network slice instance "slice-7" with
+ four SDPs: SDP1, SDP2, SDP3 and SDP4 with A2A connectivity type. All
+ SDPs are designated as customer-facing ports on the PE.
+
+ The service is realized using a single A2A Connectivity Construct,
+ and a low-bandwidth "slo-sle-template" policy applied to SDP4 and
+ SDP3, while a high-bandwidth "slo-sle-template" policy applied to
+ SDP1 and SDP2. Notice that the "slo-sle-templates" at the
+ Connectivity Construct level takes precedence over the one specified
+ at the group level.
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 99]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ +--------+ 2001:db8:0:1::1 2001:db8:0:3::1
+ |CE1 o------/ VLAN100 VLAN100
+ +--------+ | SDP1 +------+ +------+ SDP3
+ +------o| PE A +---------|-PE C | +--------+
+ | | | |-----/-----o CE3 |
+ +---+--+ +------+ +--------+
+ | |
+ | |
+ | |
+ | |
+ +--------+ 2001:db8:0:2::1 | |
+ |CE2 o------/ VLAN100 | | 2001:db8:0:4::1
+ +--------+ | SDP2 +---+--+ +---+--+ VLAN100
+ +------o| PE B +---------|PE D | SDP4 +--------+
+ | | | o-----/-----o CE4 |
+ +------+ +------+ +--------+
+
+ Figure 31: Example of An A2A Slice Service with SLO Precedence
+
+ Figure 32 shows an example YANG JSON data for the body of the Network
+ Slice Service instances request.
+
+ ============== NOTE: '\' line wrapping per RFC 8792 ===============
+
+ {
+ "ietf-network-slice-service:network-slice-services": {
+ "slo-sle-templates": {
+ "slo-sle-template": [
+ {
+ "id": "high-BW-template",
+ "description": \
+ "take the highest BW forwarding path"
+ },
+ {
+ "id": "low-BW-template",
+ "description": "lowest BW forwarding behavior"
+ }
+ ]
+ },
+ "slice-service": [
+ {
+ "id": "slice-7",
+ "description": "Foo",
+ "service-tags": {
+ "tag-type": [
+ {
+ "tag-type": "customer",
+ "tag-type-value": [
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 100]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "Customer-FOO"
+ ]
+ },
+ {
+ "tag-type": "service",
+ "tag-type-value": [
+ "L3"
+ ]
+ }
+ ]
+ },
+ "status": {
+ "admin-status": {
+ "status": "ietf-vpn-common:admin-up"
+ }
+ },
+ "sdps": {
+ "sdp": [
+ {
+ "id": "SDP1",
+ "description": "Central Office 1 at location PE-A",
+ "node-id": "PE-A",
+ "sdp-ip-address": [
+ "2001:db8:0:1::1"
+ ],
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "vlan",
+ "vlan": [
+ 100
+ ]
+ }
+ ],
+ "target-connection-group-id": "matrix1"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "AC-SDP1",
+ "description": "Device 1 to PE-A",
+ "ac-node-id": "PE-A",
+ "ac-tp-id": "GigabitEthernet1/0/0/0",
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 101]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "ac-ipv6-address": "2001:db8:0:1::1",
+ "ac-ipv6-prefix-length": 64,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "100"
+ ]
+ }
+ ]
+ },
+ "incoming-qos-policy": {
+ "qos-policy-name": "QoS-Gold",
+ "rate-limits": {
+ "cir": "1000000",
+ "cbs": "1000",
+ "pir": "5000000",
+ "pbs": "1000"
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "SDP2",
+ "description": "Central Office 2 at location PE-B",
+ "node-id": "PE-B",
+ "sdp-ip-address": [
+ "2001:db8:0:2::1"
+ ],
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "vlan",
+ "vlan": [
+ 100
+ ]
+ }
+ ],
+ "target-connection-group-id": "matrix1"
+ }
+ ]
+ },
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 102]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "AC-SDP2",
+ "description": "Device 2 to PE-B",
+ "ac-node-id": "PE-B",
+ "ac-tp-id": "GigabitEthernet2/0/0/0",
+ "ac-ipv6-address": "2001:db8:0:2::1",
+ "ac-ipv6-prefix-length": 64,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "100"
+ ]
+ }
+ ]
+ },
+ "incoming-qos-policy": {
+ "qos-policy-name": "QoS-Gold",
+ "rate-limits": {
+ "cir": "1000000",
+ "cbs": "1000",
+ "pir": "5000000",
+ "pbs": "1000"
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "SDP3",
+ "description": "Remote Office 1 at location PE-C",
+ "node-id": "PE-C",
+ "sdp-ip-address": [
+ "2001:db8:0:3::1"
+ ],
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "vlan",
+ "vlan": [
+ 100
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 103]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ ]
+ }
+ ],
+ "target-connection-group-id": "matrix1"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "AC-SDP3",
+ "description": "Device 3 to PE-C",
+ "ac-node-id": "PE-C",
+ "ac-tp-id": "GigabitEthernet3/0/0/0",
+ "ac-ipv6-address": "2001:db8:0:3::1",
+ "ac-ipv6-prefix-length": 64,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "100"
+ ]
+ }
+ ]
+ },
+ "incoming-qos-policy": {
+ "qos-policy-name": "QoS-Gold",
+ "rate-limits": {
+ "cir": "1000000",
+ "cbs": "1000",
+ "pir": "5000000",
+ "pbs": "1000"
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "SDP4",
+ "description": "Remote Office 2 at location PE-D",
+ "node-id": "PE-D",
+ "sdp-ip-address": [
+ "2001:db8:0:4::1"
+ ],
+ "service-match-criteria": {
+ "match-criterion": [
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 104]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "vlan",
+ "vlan": [
+ 100
+ ]
+ }
+ ],
+ "target-connection-group-id": "matrix1"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "AC-SDP4",
+ "description": "Device 4 to PE-D",
+ "ac-node-id": "PE-A",
+ "ac-tp-id": "GigabitEthernet4/0/0/0",
+ "ac-ipv6-address": "2001:db8:0:4::1",
+ "ac-ipv6-prefix-length": 64,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "100"
+ ]
+ }
+ ]
+ },
+ "incoming-qos-policy": {
+ "qos-policy-name": "QoS-Gold",
+ "rate-limits": {
+ "cir": "1000000",
+ "cbs": "1000",
+ "pir": "5000000",
+ "pbs": "1000"
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+ },
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 105]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "connection-groups": {
+ "connection-group": [
+ {
+ "id": "matrix1",
+ "slo-sle-template": "low-BW-template",
+ "connectivity-construct": [
+ {
+ "id": "1",
+ "a2a-sdp": [
+ {
+ "sdp-id": "SDP1",
+ "slo-sle-template": "high-BW-template"
+ },
+ {
+ "sdp-id": "SDP2",
+ "slo-sle-template": "high-BW-template"
+ },
+ {
+ "sdp-id": "SDP3"
+ },
+ {
+ "sdp-id": "SDP4"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+
+
+ Figure 32: Example of a Message Body to Create an A2A Slice
+ Service with SLO Precedence
+
+B.6. Example-6: SDP at CE, L3 A2A Slice Service
+
+ Figure 33 shows an example of one Network slice instance where the
+ SDPs are located at the PE-facing ports on the CE:
+
+ * Network Slice 8 with SDP31 on CE Device1, SDP33 (with two ACs) on
+ Device 3 and SDP34 on Device 4, with an A2A connectivity type.
+ This is an L3 Slice Service that uses the uniform low-latency slo-
+ sle-template policy between all SDPs.
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 106]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ * This example also introduces the optional attribute of "sdp-ip",
+ which could be a loopback interface on the device. How this "sdp-
+ ip" is used by the NSC is out-of-scope here, but, for example,
+ this could be the management interface of the device. The SDP and
+ AC details are from the perspective of the CE in this example.
+ How the CE ACs are mapped to the PE ACs is up to the NSC
+ implementation and out-of-scope in this example.
+
+ SDP31 AC "id"=ac31, "node-id"=Device1, interface: GigabitEthernet0
+ vlan 100
+
+ SDP33 AC "id"=ac33a, "node-id"=Device3, interface:
+ GigabitEthernet0 vlan 101
+
+ SDP33 AC "id"=ac33b, "node-id"=Device3, interface:
+ GigabitEthernet1 vlan 201
+
+ SDP34 AC "id"=ac34, "node-id"=Device4, interface: GigabitEthernet3
+ vlan 100
+
+ SDP31
+ SDP-ip 203.0.113.1
+ (Loopback)
+ |
+ | 192.0.2.2/26
+ v VLAN100 +------+
+ +--------+ ac31 | PE A +-------------+
+ |Device1 o-------/-----o| | | SDP34
+ +--------+ +---+--+ | SDP-ip 203.0.113.129
+ | | |
+ SDP33 | | |
+ SDP-ip 203.0.113.65 | +---+--+ v
+ | 192.0.2.66/26 | | | +--------+
+ v VLAN101 | | PE C o-----/-----o Device4|
+ +--------+ ac33a | +---+--+ ac34 +--------+
+ | o------/ | | VLAN100
+ | | | +---+---+ | 198.51.100.66/26
+ |Device3 | +------o| PE B +------------+
+ | o-------/-----o| |
+ +--------+ ac33b +-------+
+ VLAN201
+ 198.51.100.2/26
+
+ Figure 33: Example of an A2A Slice Service with CE Based SDP
+
+ Figure 34 shows an example YANG JSON data for the body of the Network
+ Slice Service instances request.
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 107]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ ============== NOTE: '\' line wrapping per RFC 8792 ===============
+
+ {
+ "ietf-network-slice-service:network-slice-services": {
+ "slo-sle-templates": {
+ "slo-sle-template": [
+ {
+ "id": "high-BW-template",
+ "description": \
+ "take the highest BW forwarding path"
+ },
+ {
+ "id": "low-latency-template",
+ "description": \
+ "lowest possible latency forwarding behavior"
+ }
+ ]
+ },
+ "slice-service": [
+ {
+ "id": "slice8",
+ "description": "slice-8",
+ "service-tags": {
+ "tag-type": [
+ {
+ "tag-type": "service",
+ "tag-type-value": [
+ "L3"
+ ]
+ }
+ ]
+ },
+ "slo-sle-template": "low-latency-template",
+ "status": {
+ "admin-status": {
+ "status": "ietf-vpn-common:admin-up"
+ }
+ },
+ "sdps": {
+ "sdp": [
+ {
+ "id": "31",
+ "node-id": "Device-1",
+ "sdp-ip-address": [
+ "203.0.113.1"
+ ],
+ "service-match-criteria": {
+ "match-criterion": [
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 108]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix1",
+ "target-connectivity-construct-id": "1"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac31",
+ "description": "AC1 connected to PE-A",
+ "ac-node-id": "Device-1",
+ "ac-tp-id": "GigabitEthernet0",
+ "ac-ipv4-address": "192.0.2.2",
+ "ac-ipv4-prefix-length": 26,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "100"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "33",
+ "node-id": "Device-3",
+ "sdp-ip-address": [
+ "203.0.113.65"
+ ],
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "any"
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 109]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ }
+ ],
+ "target-connection-group-id": "matrix1",
+ "target-connectivity-construct-id": "1"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac33a",
+ "description": "AC33a connected to PE-B",
+ "ac-node-id": "Device-3",
+ "ac-tp-id": "GigabitEthernet0",
+ "ac-ipv4-address": "192.0.2.66",
+ "ac-ipv4-prefix-length": 26,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "101"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "id": "ac33b",
+ "description": "AC33b connected to PE-B",
+ "ac-node-id": "Device-3",
+ "ac-tp-id": "GigabitEthernet1",
+ "ac-ipv4-address": "198.51.100.2",
+ "ac-ipv4-prefix-length": 26,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "201"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 110]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ {
+ "id": "34",
+ "node-id": "Device-4",
+ "sdp-ip-address": [
+ "203.0.113.129"
+ ],
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix1",
+ "target-connectivity-construct-id": "1"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac34",
+ "description": "AC34 connected to PE-C",
+ "ac-node-id": "Device-4",
+ "ac-tp-id": "GigabitEthernet3",
+ "ac-ipv4-address": "198.51.100.66",
+ "ac-ipv4-prefix-length": 26,
+ "ac-tags": {
+ "ac-tag": [
+ {
+ "tag-type": "vlan-id",
+ "tag-type-value": [
+ "100"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "connection-groups": {
+ "connection-group": [
+ {
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 111]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "id": "matrix1",
+ "connectivity-type": "ietf-vpn-common:any-to-any",
+ "connectivity-construct": [
+ {
+ "id": "1",
+ "a2a-sdp": [
+ {
+ "sdp-id": "31"
+ },
+ {
+ "sdp-id": "33"
+ },
+ {
+ "sdp-id": "34"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+
+
+ Figure 34: Example of a Message Body to Create an CE based A2A
+ Slice Services
+
+B.7. Example-7: SDP at CE, L3 A2A Slice Service with Network
+ Abstraction
+
+ Figure 35 shows an example of one Network slice instance where the
+ SDPs are located at the PE-facing ports on the CE.
+
+ In this example, it is assumed that the NSC already has circuit
+ binding details between the CE and PE which were previously assigned
+ (method is out-of-scope) or the NSC has mechanisms to determine this
+ mapping. While the NSC capabilities are out-of-scope of this
+ document, the NSC may use the CE device name, "sdp-id", "sdp-ip", AC
+ "id" or the "peer-sap-id" to complete this AC circuit binding.
+
+ This example introduces the "peer-sap-id", which in this case, is an
+ operator provided identifier that the slice requester can use for the
+ NSC to identify the service attachment point (saps) in an abstracted
+ way. How the NSC uses the "peer-sap-id" is out of scope of this
+ document, but a possible implementation would be that the NSC was
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 112]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ previously provisioned with a "peer-sap-id" to PE device/interface/
+ VLAN mapping table. Alternatively, the NSC can request this mapping
+ from an external database.
+
+ * Network Slice 9 with SDP31 on CPE Device1, SDP33 (with two ACs) on
+ Device 3 and SDP34 on Device 4, with an A2A connectivity type.
+ This is an L3 Slice Service that uses the uniform low-latency slo-
+ sle-template policy between all SDPs.
+
+ SDP31 AC "id"=ac31, "node-id"=Device1, "peer-sap-id"= foo.com-
+ circuitID-12345
+
+ SDP33 AC "id"=ac33a, "node-id"=Device3, "peer-sap-id"=foo.com-
+ circuitID-67890
+
+ SDP33 AC "id"=ac33b, "node-id"=Device3, "peer-sap-id"=foo.com-
+ circuitID-54321ABC
+
+ SDP34 AC "id"=ac34, "node-id"=Device4, "peer-sap-id"=foo.com-
+ circuitID-9876
+
+ SDP31
+ 2001:db8:0:1::1
+ (Loopback,etc)
+ |
+ |
+ v +-----------------------+
+ +--------+ ac31 | |
+ |Device1 o-------/-----o|sap | SDP34
+ +--------+ | | 2001:db8:0:3::1
+ | Abstracted | |
+ SDP33 | Provider Network | |
+ 2001:db8:0:2::1 | | v
+ | | | +--------+
+ v | sap|-----/-----o Device4|
+ +--------+ ac33a | | ac41 +--------+
+ | o------/ | |
+ | | | | |
+ |Device3 | +------o|sap |
+ | o-------/-----o|sap |
+ +--------+ ac33b +-----------------------+
+
+ Figure 35: Example of a Message Body to Create an A2A CE Based
+ Slice Service with Abstraction
+
+ Figure 36 shows an example YANG JSON data for the body of the Network
+ Slice Service instances request.
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 113]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ ============== NOTE: '\' line wrapping per RFC 8792 ===============
+
+ {
+ "ietf-network-slice-service:network-slice-services": {
+ "slo-sle-templates": {
+ "slo-sle-template": [
+ {
+ "id": "high-BW-template",
+ "description": "take the highest BW forwarding path"
+ },
+ {
+ "id": "low-latency-template",
+ "description": \
+ "lowest possible latency forwarding behavior"
+ }
+ ]
+ },
+ "slice-service": [
+ {
+ "id": "slice-9",
+ "description": "example slice7",
+ "service-tags": {
+ "tag-type": [
+ {
+ "tag-type": "service",
+ "tag-type-value": [
+ "L3"
+ ]
+ }
+ ]
+ },
+ "slo-sle-template": "low-latency-template",
+ "status": {
+ "admin-status": {
+ "status": "ietf-vpn-common:admin-up"
+ }
+ },
+ "sdps": {
+ "sdp": [
+ {
+ "id": "31",
+ "node-id": "Device-1",
+ "sdp-ip-address": [
+ "2001:db8:0:1::1"
+ ],
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 114]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ "index": 1,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix1"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac31",
+ "sdp-peering": {
+ "peer-sap-id": "foo.com-circuitID-12345"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "33",
+ "node-id": "Device-3",
+ "sdp-ip-address": [
+ "2001:db8:0:2::1"
+ ],
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix1",
+ "target-connectivity-construct-id": "1"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac33a",
+ "sdp-peering": {
+ "peer-sap-id": "foo.com-circuitID-67890"
+ }
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 115]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ },
+ {
+ "id": "ac33b",
+ "sdp-peering": {
+ "peer-sap-id": "foo.com-circuitID-54321ABC"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "34",
+ "node-id": "Device-4",
+ "sdp-ip-address": [
+ "2001:db8:0:3::1"
+ ],
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": [
+ {
+ "type": "any"
+ }
+ ],
+ "target-connection-group-id": "matrix1"
+ }
+ ]
+ },
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "ac34",
+ "sdp-peering": {
+ "peer-sap-id": "foo.com-circuitID-9876"
+ }
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "connection-groups": {
+ "connection-group": [
+ {
+ "id": "matrix1",
+ "connectivity-type": "ietf-vpn-common:any-to-any",
+ "connectivity-construct": [
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 116]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ {
+ "id": "1",
+ "a2a-sdp": [
+ {
+ "sdp-id": "31"
+ },
+ {
+ "sdp-id": "33"
+ },
+ {
+ "sdp-id": "34"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+
+
+ Figure 36: Example of a Message Body to Create an A2A Slice
+ Service with Abstraction
+
+Appendix C. Complete Model Tree Structure
+
+ module: ietf-network-slice-service
+ +--rw network-slice-services
+ +--rw slo-sle-templates
+ | +--rw slo-sle-template* [id]
+ | +--rw id string
+ | +--rw description? string
+ | +--rw template-ref? slice-template-ref
+ | +--rw slo-policy
+ | | +--rw metric-bound* [metric-type]
+ | | | +--rw metric-type identityref
+ | | | +--rw metric-unit string
+ | | | +--rw value-description? string
+ | | | +--rw percentile-value? percentile
+ | | | +--rw bound? uint64
+ | | +--rw availability? identityref
+ | | +--rw mtu? uint32
+ | +--rw sle-policy
+ | +--rw security* identityref
+ | +--rw isolation* identityref
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 117]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ | +--rw max-occupancy-level? uint8
+ | +--rw path-constraints
+ | +--rw service-functions
+ | +--rw diversity
+ | +--rw diversity-type?
+ | te-types:te-path-disjointness
+ +--rw slice-service* [id]
+ +--rw id string
+ +--rw description? string
+ +--rw service-tags
+ | +--rw tag-type* [tag-type]
+ | +--rw tag-type identityref
+ | +--rw tag-type-value* string
+ +--rw (slo-sle-policy)?
+ | +--:(standard)
+ | | +--rw slo-sle-template? slice-template-ref
+ | +--:(custom)
+ | +--rw service-slo-sle-policy
+ | +--rw description? string
+ | +--rw slo-policy
+ | | +--rw metric-bound* [metric-type]
+ | | | +--rw metric-type identityref
+ | | | +--rw metric-unit string
+ | | | +--rw value-description? string
+ | | | +--rw percentile-value? percentile
+ | | | +--rw bound? uint64
+ | | +--rw availability? identityref
+ | | +--rw mtu? uint32
+ | +--rw sle-policy
+ | +--rw security* identityref
+ | +--rw isolation* identityref
+ | +--rw max-occupancy-level? uint8
+ | +--rw path-constraints
+ | +--rw service-functions
+ | +--rw diversity
+ | +--rw diversity-type?
+ | te-types:te-path-disjointness
+ +--rw test-only? empty
+ +--rw status
+ | +--rw admin-status
+ | | +--rw status? identityref
+ | | +--ro last-change? yang:date-and-time
+ | +--ro oper-status
+ | +--ro status? identityref
+ | +--ro last-change? yang:date-and-time
+ +--rw sdps
+ | +--rw sdp* [id]
+ | +--rw id string
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 118]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ | +--rw description? string
+ | +--rw geo-location
+ | | +--rw reference-frame
+ | | | +--rw alternate-system? string
+ | | | | {alternate-systems}?
+ | | | +--rw astronomical-body? string
+ | | | +--rw geodetic-system
+ | | | +--rw geodetic-datum? string
+ | | | +--rw coord-accuracy? decimal64
+ | | | +--rw height-accuracy? decimal64
+ | | +--rw (location)?
+ | | | +--:(ellipsoid)
+ | | | | +--rw latitude? decimal64
+ | | | | +--rw longitude? decimal64
+ | | | | +--rw height? decimal64
+ | | | +--:(cartesian)
+ | | | +--rw x? decimal64
+ | | | +--rw y? decimal64
+ | | | +--rw z? decimal64
+ | | +--rw velocity
+ | | | +--rw v-north? decimal64
+ | | | +--rw v-east? decimal64
+ | | | +--rw v-up? decimal64
+ | | +--rw timestamp? yang:date-and-time
+ | | +--rw valid-until? yang:date-and-time
+ | +--rw node-id? string
+ | +--rw sdp-ip-address* inet:ip-address
+ | +--rw tp-ref? leafref
+ | +--rw service-match-criteria
+ | | +--rw match-criterion* [index]
+ | | +--rw index uint32
+ | | +--rw match-type* [type]
+ | | | +--rw type identityref
+ | | | +--rw (value)?
+ | | | +--:(interface)
+ | | | | +--rw interface-name* string
+ | | | +--:(vlan)
+ | | | | +--rw vlan* uint16
+ | | | +--:(label)
+ | | | | +--rw label*
+ | | | | rt-types:mpls-label
+ | | | +--:(ip-prefix)
+ | | | | +--rw ip-prefix* inet:ip-prefix
+ | | | +--:(dscp)
+ | | | | +--rw dscp* inet:dscp
+ | | | +--:(acl)
+ | | | +--rw acl-name* string
+ | | +--rw target-connection-group-id leafref
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 119]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ | | +--rw connection-group-sdp-role?
+ | | | identityref
+ | | +--rw target-connectivity-construct-id? leafref
+ | +--rw incoming-qos-policy
+ | | +--rw qos-policy-name? string
+ | | +--rw rate-limits
+ | | +--rw cir? uint64
+ | | +--rw cbs? uint64
+ | | +--rw eir? uint64
+ | | +--rw ebs? uint64
+ | | +--rw pir? uint64
+ | | +--rw pbs? uint64
+ | | +--rw classes
+ | | +--rw cos* [cos-id]
+ | | +--rw cos-id uint8
+ | | +--rw cir? uint64
+ | | +--rw cbs? uint64
+ | | +--rw eir? uint64
+ | | +--rw ebs? uint64
+ | | +--rw pir? uint64
+ | | +--rw pbs? uint64
+ | +--rw outgoing-qos-policy
+ | | +--rw qos-policy-name? string
+ | | +--rw rate-limits
+ | | +--rw cir? uint64
+ | | +--rw cbs? uint64
+ | | +--rw eir? uint64
+ | | +--rw ebs? uint64
+ | | +--rw pir? uint64
+ | | +--rw pbs? uint64
+ | | +--rw classes
+ | | +--rw cos* [cos-id]
+ | | +--rw cos-id uint8
+ | | +--rw cir? uint64
+ | | +--rw cbs? uint64
+ | | +--rw eir? uint64
+ | | +--rw ebs? uint64
+ | | +--rw pir? uint64
+ | | +--rw pbs? uint64
+ | +--rw sdp-peering
+ | | +--rw peer-sap-id* string
+ | | +--rw protocols
+ | +--rw ac-svc-ref*
+ | | ac-svc:attachment-circuit-reference
+ | +--rw ce-mode? boolean
+ | +--rw attachment-circuits
+ | | +--rw attachment-circuit* [id]
+ | | +--rw id string
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 120]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ | | +--rw description? string
+ | | +--rw ac-svc-ref?
+ | | | ac-svc:attachment-circuit-reference
+ | | +--rw ac-node-id? string
+ | | +--rw ac-tp-id? string
+ | | +--rw ac-ipv4-address? inet:ipv4-address
+ | | +--rw ac-ipv4-prefix-length? uint8
+ | | +--rw ac-ipv6-address? inet:ipv6-address
+ | | +--rw ac-ipv6-prefix-length? uint8
+ | | +--rw mtu? uint32
+ | | +--rw ac-tags
+ | | | +--rw ac-tag* [tag-type]
+ | | | +--rw tag-type identityref
+ | | | +--rw tag-type-value* string
+ | | +--rw incoming-qos-policy
+ | | | +--rw qos-policy-name? string
+ | | | +--rw rate-limits
+ | | | +--rw cir? uint64
+ | | | +--rw cbs? uint64
+ | | | +--rw eir? uint64
+ | | | +--rw ebs? uint64
+ | | | +--rw pir? uint64
+ | | | +--rw pbs? uint64
+ | | | +--rw classes
+ | | | +--rw cos* [cos-id]
+ | | | +--rw cos-id uint8
+ | | | +--rw cir? uint64
+ | | | +--rw cbs? uint64
+ | | | +--rw eir? uint64
+ | | | +--rw ebs? uint64
+ | | | +--rw pir? uint64
+ | | | +--rw pbs? uint64
+ | | +--rw outgoing-qos-policy
+ | | | +--rw qos-policy-name? string
+ | | | +--rw rate-limits
+ | | | +--rw cir? uint64
+ | | | +--rw cbs? uint64
+ | | | +--rw eir? uint64
+ | | | +--rw ebs? uint64
+ | | | +--rw pir? uint64
+ | | | +--rw pbs? uint64
+ | | | +--rw classes
+ | | | +--rw cos* [cos-id]
+ | | | +--rw cos-id uint8
+ | | | +--rw cir? uint64
+ | | | +--rw cbs? uint64
+ | | | +--rw eir? uint64
+ | | | +--rw ebs? uint64
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 121]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ | | | +--rw pir? uint64
+ | | | +--rw pbs? uint64
+ | | +--rw sdp-peering
+ | | | +--rw peer-sap-id? string
+ | | | +--rw protocols
+ | | +--rw status
+ | | +--rw admin-status
+ | | | +--rw status? identityref
+ | | | +--ro last-change? yang:date-and-time
+ | | +--ro oper-status
+ | | +--ro status? identityref
+ | | +--ro last-change? yang:date-and-time
+ | +--rw status
+ | | +--rw admin-status
+ | | | +--rw status? identityref
+ | | | +--ro last-change? yang:date-and-time
+ | | +--ro oper-status
+ | | +--ro status? identityref
+ | | +--ro last-change? yang:date-and-time
+ | +--ro sdp-monitoring
+ | +--ro incoming-bw-value? yang:gauge64
+ | +--ro incoming-bw-percent? percentage
+ | +--ro outgoing-bw-value? yang:gauge64
+ | +--ro outgoing-bw-percent? percentage
+ +--rw connection-groups
+ | +--rw connection-group* [id]
+ | +--rw id string
+ | +--rw connectivity-type? identityref
+ | +--rw (slo-sle-policy)?
+ | | +--:(standard)
+ | | | +--rw slo-sle-template?
+ | | | slice-template-ref
+ | | +--:(custom)
+ | | +--rw service-slo-sle-policy
+ | | +--rw description? string
+ | | +--rw slo-policy
+ | | | +--rw metric-bound* [metric-type]
+ | | | | +--rw metric-type identityref
+ | | | | +--rw metric-unit string
+ | | | | +--rw value-description? string
+ | | | | +--rw percentile-value? percentile
+ | | | | +--rw bound? uint64
+ | | | +--rw availability? identityref
+ | | | +--rw mtu? uint32
+ | | +--rw sle-policy
+ | | +--rw security* identityref
+ | | +--rw isolation* identityref
+ | | +--rw max-occupancy-level? uint8
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 122]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ | | +--rw path-constraints
+ | | +--rw service-functions
+ | | +--rw diversity
+ | | +--rw diversity-type?
+ | | te-types:te-path-disjointness
+ | +--rw service-slo-sle-policy-override? identityref
+ | +--rw connectivity-construct* [id]
+ | | +--rw id string
+ | | +--rw (type)?
+ | | | +--:(p2p)
+ | | | | +--rw p2p-sender-sdp?
+ | | | | | -> ../../../../sdps/sdp/id
+ | | | | +--rw p2p-receiver-sdp?
+ | | | | -> ../../../../sdps/sdp/id
+ | | | +--:(p2mp)
+ | | | | +--rw p2mp-sender-sdp?
+ | | | | | -> ../../../../sdps/sdp/id
+ | | | | +--rw p2mp-receiver-sdp*
+ | | | | -> ../../../../sdps/sdp/id
+ | | | +--:(a2a)
+ | | | +--rw a2a-sdp* [sdp-id]
+ | | | +--rw sdp-id
+ | | | | -> ../../../../../sdps/sdp/id
+ | | | +--rw (slo-sle-policy)?
+ | | | +--:(standard)
+ | | | | +--rw slo-sle-template?
+ | | | | slice-template-ref
+ | | | +--:(custom)
+ | | | +--rw service-slo-sle-policy
+ | | | +--rw description? string
+ | | | +--rw slo-policy
+ | | | | +--rw metric-bound*
+ | | | | | [metric-type]
+ | | | | | +--rw metric-type
+ | | | | | | identityref
+ | | | | | +--rw metric-unit
+ | | | | | | string
+ | | | | | +--rw value-description?
+ | | | | | | string
+ | | | | | +--rw percentile-value?
+ | | | | | | percentile
+ | | | | | +--rw bound?
+ | | | | | uint64
+ | | | | +--rw availability?
+ | | | | | identityref
+ | | | | +--rw mtu? uint32
+ | | | +--rw sle-policy
+ | | | +--rw security*
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 123]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ | | | | identityref
+ | | | +--rw isolation*
+ | | | | identityref
+ | | | +--rw max-occupancy-level?
+ | | | | uint8
+ | | | +--rw path-constraints
+ | | | +--rw service-functions
+ | | | +--rw diversity
+ | | | +--rw diversity-type?
+ | | | te-types:
+ te-path-disjointness
+ | | +--rw (slo-sle-policy)?
+ | | | +--:(standard)
+ | | | | +--rw slo-sle-template?
+ | | | | slice-template-ref
+ | | | +--:(custom)
+ | | | +--rw service-slo-sle-policy
+ | | | +--rw description? string
+ | | | +--rw slo-policy
+ | | | | +--rw metric-bound* [metric-type]
+ | | | | | +--rw metric-type
+ | | | | | | identityref
+ | | | | | +--rw metric-unit string
+ | | | | | +--rw value-description? string
+ | | | | | +--rw percentile-value? percentile
+ | | | | | +--rw bound? uint64
+ | | | | +--rw availability? identityref
+ | | | | +--rw mtu? uint32
+ | | | +--rw sle-policy
+ | | | +--rw security* identityref
+ | | | +--rw isolation* identityref
+ | | | +--rw max-occupancy-level? uint8
+ | | | +--rw path-constraints
+ | | | +--rw service-functions
+ | | | +--rw diversity
+ | | | +--rw diversity-type?
+ | | | te-types:
+ te-path-disjointness
+ | | +--rw service-slo-sle-policy-override?
+ | | | identityref
+ | | +--rw status
+ | | | +--rw admin-status
+ | | | | +--rw status? identityref
+ | | | | +--ro last-change? yang:date-and-time
+ | | | +--ro oper-status
+ | | | +--ro status? identityref
+ | | | +--ro last-change? yang:date-and-time
+ | | +--ro connectivity-construct-monitoring
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 124]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ | | +--ro one-way-min-delay? yang:gauge64
+ | | +--ro one-way-max-delay? yang:gauge64
+ | | +--ro one-way-delay-variation? yang:gauge64
+ | | +--ro one-way-packet-loss? decimal64
+ | | +--ro two-way-min-delay? yang:gauge64
+ | | +--ro two-way-max-delay? yang:gauge64
+ | | +--ro two-way-delay-variation? yang:gauge64
+ | | +--ro two-way-packet-loss? decimal64
+ | +--ro connection-group-monitoring
+ | +--ro one-way-min-delay? yang:gauge64
+ | +--ro one-way-max-delay? yang:gauge64
+ | +--ro one-way-delay-variation? yang:gauge64
+ | +--ro one-way-packet-loss? decimal64
+ | +--ro two-way-min-delay? yang:gauge64
+ | +--ro two-way-max-delay? yang:gauge64
+ | +--ro two-way-delay-variation? yang:gauge64
+ | +--ro two-way-packet-loss? decimal64
+ +--rw custom-topology
+ +--rw network-ref? -> /nw:networks/network/network-id
+
+Appendix D. Comparison with the Design Choice of ACTN VN Model
+ Augmentation
+
+ The difference between the ACTN VN model and the Network Slice
+ Service requirements is that the Network Slice Service interface is a
+ technology-agnostic interface, whereas the VN model is bound to the
+ TE Topologies. The realization of the Network Slice does not
+ necessarily require the slice network to support the TE technology.
+
+ The ACTN VN (Virtual Network) model introduced in [RFC9731] is the
+ abstract customer view of the TE network. Its YANG structure
+ includes four components:
+
+ * VN: A Virtual Network (VN) is a network provided by a service
+ provider to a customer for use and two types of VN have been
+ defined. The Type 1 VN can be seen as a set of edge-to-edge
+ abstract links. Each link is an abstraction of the underlying
+ network which can encompass edge points of the customer's network,
+ access links, intra-domain paths, and inter-domain links.
+
+ * AP: An AP is a logical identifier used to identify the access link
+ which is shared between the customer and the IETF scoped Network.
+
+ * VN-AP: A VN-AP is a logical binding between an AP and a given VN.
+
+ * VN-member: A VN-member is an abstract edge-to-edge link between
+ any two APs or VN-APs. Each link is formed as an E2E tunnel
+ across the underlying networks.
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 125]
+
+Internet-Draft Network Slice Service YANG Model May 2025
+
+
+ The Type 1 VN can be used to describe Network Slice Service
+ connection requirements. However, the Network Slice SLOs and Network
+ Slice SDPs are not clearly defined and there's no direct equivalent.
+ For example, the SLO requirement of the VN is defined through the TE
+ Topologies YANG model, but the TE Topologies model is related to a
+ specific implementation technology. Also, VN-AP does not define
+ "service-match-criteria" to specify a specific SDP belonging to a
+ Network Slice Service.
+
+Authors' Addresses
+
+ Bo Wu
+ Huawei Technologies
+ 101 Software Avenue, Yuhua District
+ Nanjing
+ Jiangsu, 210012
+ China
+ Email: lana.wubo@huawei.com
+
+
+ Dhruv Dhody
+ Huawei Technologies
+ Divyashree Techno Park
+ Bangalore 560066
+ Karnataka
+ India
+ Email: dhruv.ietf@gmail.com
+
+
+ Reza Rokui
+ Ciena
+ Email: rrokui@ciena.com
+
+
+ Tarek Saad
+ Cisco Systems, Inc
+ Email: tsaad@cisco.com
+
+
+ John Mullooly
+ Cisco Systems, Inc
+ Email: jmullool@cisco.com
+
+
+
+
+
+
+
+
+
+Wu, et al. Expires 10 November 2025 [Page 126]
diff --git a/doc/NSC/examples/ietf_green_request.json b/doc/NSC/examples/ietf_green_request.json
new file mode 100644
index 0000000000000000000000000000000000000000..9430f28824ecc3e507345095201392124c7fb261
--- /dev/null
+++ b/doc/NSC/examples/ietf_green_request.json
@@ -0,0 +1,172 @@
+{
+ "ietf-network-slice-service:network-slice-services": {
+ "slo-sle-templates": {
+ "slo-sle-template": [
+ {
+ "id": "B",
+ "description": "",
+ "slo-policy": {
+ "metric-bound": [
+ {
+ "metric-type": "energy_consumption",
+ "metric-unit": "kWh",
+ "bound": 20200
+ },
+ {
+ "metric-type": "energy_efficiency",
+ "metric-unit": "Wats/bps",
+ "bound": 6
+ },
+ {
+ "metric-type": "carbon_emission",
+ "metric-unit": "grams of CO2 per kWh",
+ "bound": 750
+ },
+ {
+ "metric-type": "renewable_energy_usage",
+ "metric-unit": "rate",
+ "bound": 0.5
+ }
+ ]
+ },
+ "sle-policy": {
+ "security": "",
+ "isolation": "",
+ "path-constraints": {
+ "service-functions": "",
+ "diversity": {
+ "diversity": {
+ "diversity-type": ""
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "slice-service": [
+ {
+ "id": "slice-service-88a585f7-a432-4312-8774-6210fb0b2342",
+ "description": "Transport network slice mapped with 3GPP slice NetworkSlice1",
+ "service-tags": {
+ "tag-type": [
+ {
+ "tag-type": "service",
+ "tag-type-value": [
+ "L2"
+ ]
+ }
+ ]
+ },
+ "slo-sle-policy": {
+ "slo-sle-template": "B"
+ },
+ "status": {},
+ "sdps": {
+ "sdp": [
+ {
+ "id": "CU-N32",
+ "geo-location": "",
+ "node-id": "A",
+ "sdp-ip-address": "10.60.11.3",
+ "tp-ref": "",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": "VLAN",
+ "value": "101",
+ "target-connection-group-id": "A_B"
+ }
+ ]
+ },
+ "incoming-qos-policy": "",
+ "outgoing-qos-policy": "",
+ "sdp-peering": {
+ "peer-sap-id": "",
+ "protocols": ""
+ },
+ "ac-svc-ref": [],
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "100",
+ "ac-ipv4-address": "10.60.11.3",
+ "ac-ipv4-prefix-length": 0,
+ "sdp-peering": {
+ "peer-sap-id": "4.4.4.4"
+ },
+ "status": {}
+ }
+ ]
+ },
+ "status": {},
+ "sdp-monitoring": ""
+ },
+ {
+ "id": "UPF-N32",
+ "geo-location": "",
+ "node-id": "B",
+ "sdp-ip-address": "10.60.10.6",
+ "tp-ref": "",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": "VLAN",
+ "value": "101",
+ "target-connection-group-id": "A_B"
+ }
+ ]
+ },
+ "incoming-qos-policy": "",
+ "outgoing-qos-policy": "",
+ "sdp-peering": {
+ "peer-sap-id": "",
+ "protocols": ""
+ },
+ "ac-svc-ref": [],
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "200",
+ "ac-ipv4-address": "10.60.10.6",
+ "ac-ipv4-prefix-length": 0,
+ "sdp-peering": {
+ "peer-sap-id": "5.5.5.5"
+ },
+ "status": {}
+ }
+ ]
+ },
+ "status": {},
+ "sdp-monitoring": ""
+ }
+ ]
+ },
+ "connection-groups": {
+ "connection-group": [
+ {
+ "id": "A_B",
+ "connectivity-type": "ietf-vpn-common:any-to-any",
+ "connectivity-construct": [
+ {
+ "id": 1,
+ "a2a-sdp": [
+ {
+ "sdp-id": "CU-N32"
+ },
+ {
+ "sdp-id": "UPF-N32"
+ }
+ ]
+ }
+ ],
+ "status": {}
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/doc/NSC/examples/slice_request_backhaul_control.json b/doc/NSC/examples/slice_request_backhaul_control.json
new file mode 100644
index 0000000000000000000000000000000000000000..f2150783ae098dc5e9511a986eb60c04f046282f
--- /dev/null
+++ b/doc/NSC/examples/slice_request_backhaul_control.json
@@ -0,0 +1,162 @@
+{
+ "ietf-network-slice-service:network-slice-services": {
+ "slo-sle-templates": {
+ "slo-sle-template": [
+ {
+ "id": "A",
+ "description": "",
+ "slo-policy": {
+ "metric-bound": [
+ {
+ "metric-type": "one-way-bandwidth",
+ "metric-unit": "kbps",
+ "bound": 2000
+ },
+ {
+ "metric-type": "one-way-delay-maximum",
+ "metric-unit": "milliseconds",
+ "bound": 5
+ }
+ ]
+ },
+ "sle-policy": {
+ "security": "",
+ "isolation": "",
+ "path-constraints": {
+ "service-functions": "",
+ "diversity": {
+ "diversity": {
+ "diversity-type": ""
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "slice-service": [
+ {
+ "id": "slice-service-11327140-7361-41b3-aa45-e84a7fb40be9",
+ "description": "Transport network slice mapped with 3GPP slice NetworkSlice1",
+ "service-tags": {
+ "tag-type": [
+ {
+ "tag-type": "service",
+ "tag-type-value": [
+ "L2"
+ ]
+ }
+ ]
+ },
+ "slo-sle-policy": {
+ "slo-sle-template": "A"
+ },
+ "status": {},
+ "sdps": {
+ "sdp": [
+ {
+ "id": "",
+ "geo-location": "",
+ "node-id": "CU-N2",
+ "sdp-ip-address": "10.60.11.3",
+ "tp-ref": "",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": "VLAN",
+ "value": "100",
+ "target-connection-group-id": "CU-N2_AMF-N2"
+ }
+ ]
+ },
+ "incoming-qos-policy": "",
+ "outgoing-qos-policy": "",
+ "sdp-peering": {
+ "peer-sap-id": "",
+ "protocols": ""
+ },
+ "ac-svc-ref": [],
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "100",
+ "ac-ipv4-address": "10.60.11.3",
+ "ac-ipv4-prefix-length": 0,
+ "sdp-peering": {
+ "peer-sap-id": "1.1.1.1"
+ },
+ "status": {}
+ }
+ ]
+ },
+ "status": {},
+ "sdp-monitoring": ""
+ },
+ {
+ "id": "",
+ "geo-location": "",
+ "node-id": "AMF-N2",
+ "sdp-ip-address": "10.60.60.105",
+ "tp-ref": "",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": "VLAN",
+ "value": "100",
+ "target-connection-group-id": "CU-N2_AMF-N2"
+ }
+ ]
+ },
+ "incoming-qos-policy": "",
+ "outgoing-qos-policy": "",
+ "sdp-peering": {
+ "peer-sap-id": "",
+ "protocols": ""
+ },
+ "ac-svc-ref": [],
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "200",
+ "ac-ipv4-address": "10.60.60.105",
+ "ac-ipv4-prefix-length": 0,
+ "sdp-peering": {
+ "peer-sap-id": "3.3.3.3"
+ },
+ "status": {}
+ }
+ ]
+ },
+ "status": {},
+ "sdp-monitoring": ""
+ }
+ ]
+ },
+ "connection-groups": {
+ "connection-group": [
+ {
+ "id": "CU-N2_AMF-N2",
+ "connectivity-type": "ietf-vpn-common:any-to-any",
+ "connectivity-construct": [
+ {
+ "id": 1,
+ "a2a-sdp": [
+ {
+ "sdp-id": "01"
+ },
+ {
+ "sdp-id": "02"
+ }
+ ]
+ }
+ ],
+ "status": {}
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
\ No newline at end of file
diff --git a/doc/NSC/examples/slice_request_backhaul_user.json b/doc/NSC/examples/slice_request_backhaul_user.json
new file mode 100644
index 0000000000000000000000000000000000000000..efd1666585ae347a775fb438fbdbd3432568f269
--- /dev/null
+++ b/doc/NSC/examples/slice_request_backhaul_user.json
@@ -0,0 +1,164 @@
+[
+ {
+ "ietf-network-slice-service:network-slice-services": {
+ "slo-sle-templates": {
+ "slo-sle-template": [
+ {
+ "id": "C",
+ "description": "",
+ "slo-policy": {
+ "metric-bound": [
+ {
+ "metric-type": "one-way-bandwidth",
+ "metric-unit": "kbps",
+ "bound": 100
+ },
+ {
+ "metric-type": "one-way-delay-maximum",
+ "metric-unit": "milliseconds",
+ "bound": 10
+ }
+ ]
+ },
+ "sle-policy": {
+ "security": "",
+ "isolation": "",
+ "path-constraints": {
+ "service-functions": "",
+ "diversity": {
+ "diversity": {
+ "diversity-type": ""
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "slice-service": [
+ {
+ "id": "slice-service-181e303a-a051-42e5-b2f2-4060732c631f",
+ "description": "Transport network slice mapped with 3GPP slice NetworkSlice1",
+ "service-tags": {
+ "tag-type": [
+ {
+ "tag-type": "",
+ "tag-type-value": [
+ ""
+ ]
+ }
+ ]
+ },
+ "slo-sle-policy": {
+ "slo-sle-template": "C"
+ },
+ "status": {},
+ "sdps": {
+ "sdp": [
+ {
+ "id": "",
+ "geo-location": "",
+ "node-id": "CU-N31",
+ "sdp-ip-address": "10.60.11.3",
+ "tp-ref": "",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": "VLAN",
+ "value": "102",
+ "target-connection-group-id": "CU-N31_UPF-N31"
+ }
+ ]
+ },
+ "incoming-qos-policy": "",
+ "outgoing-qos-policy": "",
+ "sdp-peering": {
+ "peer-sap-id": "",
+ "protocols": ""
+ },
+ "ac-svc-ref": [],
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "100",
+ "ac-ipv4-address": "10.60.11.3",
+ "ac-ipv4-prefix-length": 0,
+ "sdp-peering": {
+ "peer-sap-id": "4.4.4.4"
+ },
+ "status": {}
+ }
+ ]
+ },
+ "status": {},
+ "sdp-monitoring": ""
+ },
+ {
+ "id": "",
+ "geo-location": "",
+ "node-id": "UPF-N31",
+ "sdp-ip-address": "10.60.60.106",
+ "tp-ref": "",
+ "service-match-criteria": {
+ "match-criterion": [
+ {
+ "index": 1,
+ "match-type": "VLAN",
+ "value": "102",
+ "target-connection-group-id": "CU-N31_UPF-N31"
+ }
+ ]
+ },
+ "incoming-qos-policy": "",
+ "outgoing-qos-policy": "",
+ "sdp-peering": {
+ "peer-sap-id": "",
+ "protocols": ""
+ },
+ "ac-svc-ref": [],
+ "attachment-circuits": {
+ "attachment-circuit": [
+ {
+ "id": "200",
+ "ac-ipv4-address": "10.60.60.106",
+ "ac-ipv4-prefix-length": 0,
+ "sdp-peering": {
+ "peer-sap-id": "5.5.5.5"
+ },
+ "status": {}
+ }
+ ]
+ },
+ "status": {},
+ "sdp-monitoring": ""
+ }
+ ]
+ },
+ "connection-groups": {
+ "connection-group": [
+ {
+ "id": "CU-N31_UPF-N31",
+ "connectivity-type": "ietf-vpn-common:any-to-any",
+ "connectivity-construct": [
+ {
+ "id": 1,
+ "a2a-sdp": [
+ {
+ "sdp-id": "01"
+ },
+ {
+ "sdp-id": "02"
+ }
+ ]
+ }
+ ],
+ "status": {}
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+]
\ No newline at end of file
diff --git a/doc/NSC/nsc_swagger.json b/doc/NSC/nsc_swagger.json
new file mode 100644
index 0000000000000000000000000000000000000000..b65ca34151413941a729007e77013f145473fb4d
--- /dev/null
+++ b/doc/NSC/nsc_swagger.json
@@ -0,0 +1,971 @@
+{
+ "swagger": "2.0",
+ "basePath": "/",
+ "paths": {
+ "/e2e/slice": {
+ "post": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "400": {
+ "description": "Invalid request format"
+ },
+ "200": {
+ "description": "No service to process."
+ },
+ "201": {
+ "description": "Slice created successfully",
+ "schema": {
+ "$ref": "#/definitions/SliceResponse"
+ }
+ }
+ },
+ "summary": "Submit a new slice request with a file",
+ "description": "This endpoint allows clients to submit transport network slice requests using a JSON payload.",
+ "operationId": "post_e2_e_slice_list",
+ "parameters": [
+ {
+ "name": "file",
+ "in": "formData",
+ "type": "file",
+ "description": "File to upload"
+ },
+ {
+ "name": "json_data",
+ "in": "formData",
+ "type": "string",
+ "description": "JSON Data in string format"
+ }
+ ],
+ "consumes": [
+ "multipart/form-data"
+ ],
+ "tags": [
+ "E2E"
+ ]
+ },
+ "delete": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "204": {
+ "description": "All transport network slices deleted successfully."
+ }
+ },
+ "summary": "Delete all slices",
+ "description": "Deletes all transport network slices from the slice controller.",
+ "operationId": "delete_e2_e_slice_list",
+ "tags": [
+ "E2E"
+ ]
+ },
+ "get": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "404": {
+ "description": "Transport network slices not found"
+ },
+ "200": {
+ "description": "Slices returned",
+ "schema": {
+ "$ref": "#/definitions/ddbb_model"
+ }
+ }
+ },
+ "summary": "Retrieve all slices",
+ "description": "Returns all transport network slices from the slice controller.",
+ "operationId": "get_e2_e_slice_list",
+ "tags": [
+ "E2E"
+ ]
+ }
+ },
+ "/e2e/slice/{slice_id}": {
+ "parameters": [
+ {
+ "description": "The ID of the slice to retrieve or modify",
+ "name": "slice_id",
+ "in": "path",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "delete": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "404": {
+ "description": "Transport network slice not found."
+ },
+ "204": {
+ "description": "Transport network slice deleted successfully."
+ }
+ },
+ "summary": "Delete a slice",
+ "description": "Deletes a specific transport network slice from the slice controller based on the provided `slice_id`.",
+ "operationId": "delete_e2_e_slice",
+ "tags": [
+ "E2E"
+ ]
+ },
+ "get": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "404": {
+ "description": "Transport network slice not found."
+ },
+ "200": {
+ "description": "Slice returned",
+ "schema": {
+ "$ref": "#/definitions/ddbb_model"
+ }
+ }
+ },
+ "summary": "Retrieve a specific slice",
+ "description": "Returns specific information related to a slice by providing its id",
+ "operationId": "get_e2_e_slice",
+ "tags": [
+ "E2E"
+ ]
+ },
+ "put": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "404": {
+ "description": "Transport network slice not found."
+ },
+ "200": {
+ "description": "Slice modified",
+ "schema": {
+ "$ref": "#/definitions/SliceResponse"
+ }
+ }
+ },
+ "summary": "Modify a slice",
+ "description": "Returns a specific slice that has been modified",
+ "operationId": "put_e2_e_slice",
+ "parameters": [
+ {
+ "name": "payload",
+ "required": true,
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/ddbb_model"
+ }
+ }
+ ],
+ "tags": [
+ "E2E"
+ ]
+ }
+ },
+ "/ixia/slice": {
+ "post": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "400": {
+ "description": "Invalid request format"
+ },
+ "200": {
+ "description": "No service to process."
+ },
+ "201": {
+ "description": "Slice created successfully",
+ "schema": {
+ "$ref": "#/definitions/SliceResponse"
+ }
+ }
+ },
+ "summary": "Submit a new slice request with a file",
+ "description": "This endpoint allows clients to submit transport network slice requests using a JSON payload.",
+ "operationId": "post_ixia_slice_list",
+ "parameters": [
+ {
+ "name": "file",
+ "in": "formData",
+ "type": "file",
+ "description": "Archivo a subir"
+ },
+ {
+ "name": "json_data",
+ "in": "formData",
+ "type": "string",
+ "description": "Datos JSON en formato string"
+ }
+ ],
+ "consumes": [
+ "multipart/form-data"
+ ],
+ "tags": [
+ "ixia"
+ ]
+ },
+ "delete": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "204": {
+ "description": "All transport network slices deleted successfully."
+ }
+ },
+ "summary": "Delete all slices",
+ "description": "Deletes all transport network slices from the slice controller.",
+ "operationId": "delete_ixia_slice_list",
+ "tags": [
+ "ixia"
+ ]
+ },
+ "get": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "404": {
+ "description": "Transport network slices not found"
+ },
+ "200": {
+ "description": "Slices returned",
+ "schema": {
+ "$ref": "#/definitions/ddbb_model"
+ }
+ }
+ },
+ "summary": "Retrieve all slices",
+ "description": "Returns all transport network slices from the slice controller.",
+ "operationId": "get_ixia_slice_list",
+ "tags": [
+ "ixia"
+ ]
+ }
+ },
+ "/ixia/slice/{slice_id}": {
+ "parameters": [
+ {
+ "description": "The ID of the slice to retrieve or modify",
+ "name": "slice_id",
+ "in": "path",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "delete": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "404": {
+ "description": "Transport network slice not found."
+ },
+ "204": {
+ "description": "Transport network slice deleted successfully."
+ }
+ },
+ "summary": "Delete a slice",
+ "description": "Deletes a specific transport network slice from the slice controller based on the provided `slice_id`.",
+ "operationId": "delete_ixia_slice",
+ "tags": [
+ "ixia"
+ ]
+ },
+ "get": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "404": {
+ "description": "Transport network slice not found."
+ },
+ "200": {
+ "description": "Slice returned",
+ "schema": {
+ "$ref": "#/definitions/ddbb_model"
+ }
+ }
+ },
+ "summary": "Retrieve a specific slice",
+ "description": "Returns specific information related to a slice by providing its id",
+ "operationId": "get_ixia_slice",
+ "tags": [
+ "ixia"
+ ]
+ },
+ "put": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "404": {
+ "description": "Transport network slice not found."
+ },
+ "200": {
+ "description": "Slice modified",
+ "schema": {
+ "$ref": "#/definitions/ddbb_model"
+ }
+ }
+ },
+ "summary": "Modify a slice",
+ "description": "Returns a specific slice that has been modified",
+ "operationId": "put_ixia_slice",
+ "parameters": [
+ {
+ "name": "payload",
+ "required": true,
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/ddbb_model"
+ }
+ }
+ ],
+ "tags": [
+ "ixia"
+ ]
+ }
+ },
+ "/tfs/slice": {
+ "post": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "400": {
+ "description": "Invalid request format"
+ },
+ "200": {
+ "description": "No service to process."
+ },
+ "201": {
+ "description": "Slice created successfully",
+ "schema": {
+ "$ref": "#/definitions/SliceResponse"
+ }
+ }
+ },
+ "summary": "Submit a new slice request with a file",
+ "description": "This endpoint allows clients to submit transport network slice requests using a JSON payload.",
+ "operationId": "post_tfs_slice_list",
+ "parameters": [
+ {
+ "name": "file",
+ "in": "formData",
+ "type": "file",
+ "description": "File to upload"
+ },
+ {
+ "name": "json_data",
+ "in": "formData",
+ "type": "string",
+ "description": "JSON Data in string format"
+ }
+ ],
+ "consumes": [
+ "multipart/form-data"
+ ],
+ "tags": [
+ "tfs"
+ ]
+ },
+ "delete": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "204": {
+ "description": "All transport network slices deleted successfully."
+ }
+ },
+ "summary": "Delete all slices",
+ "description": "Deletes all transport network slices from the slice controller.",
+ "operationId": "delete_tfs_slice_list",
+ "tags": [
+ "tfs"
+ ]
+ },
+ "get": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "404": {
+ "description": "Transport network slices not found"
+ },
+ "200": {
+ "description": "Slices returned",
+ "schema": {
+ "$ref": "#/definitions/ddbb_model"
+ }
+ }
+ },
+ "summary": "Retrieve all slices",
+ "description": "Returns all transport network slices from the slice controller.",
+ "operationId": "get_tfs_slice_list",
+ "tags": [
+ "tfs"
+ ]
+ }
+ },
+ "/tfs/slice/{slice_id}": {
+ "parameters": [
+ {
+ "description": "The ID of the slice to retrieve or modify",
+ "name": "slice_id",
+ "in": "path",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "delete": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "404": {
+ "description": "Transport network slice not found."
+ },
+ "204": {
+ "description": "Transport network slice deleted successfully."
+ }
+ },
+ "summary": "Delete a slice",
+ "description": "Deletes a specific transport network slice from the slice controller based on the provided `slice_id`.",
+ "operationId": "delete_tfs_slice",
+ "tags": [
+ "tfs"
+ ]
+ },
+ "get": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "404": {
+ "description": "Transport network slice not found."
+ },
+ "200": {
+ "description": "Slice returned",
+ "schema": {
+ "$ref": "#/definitions/ddbb_model"
+ }
+ }
+ },
+ "summary": "Retrieve a specific slice",
+ "description": "Returns specific information related to a slice by providing its id",
+ "operationId": "get_tfs_slice",
+ "tags": [
+ "tfs"
+ ]
+ },
+ "put": {
+ "responses": {
+ "500": {
+ "description": "Internal server error"
+ },
+ "404": {
+ "description": "Transport network slice not found."
+ },
+ "200": {
+ "description": "Slice modified",
+ "schema": {
+ "$ref": "#/definitions/SliceResponse"
+ }
+ }
+ },
+ "summary": "Modify a slice",
+ "description": "Returns a specific slice that has been modified",
+ "operationId": "put_tfs_slice",
+ "parameters": [
+ {
+ "name": "payload",
+ "required": true,
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/ddbb_model"
+ }
+ }
+ ],
+ "tags": [
+ "tfs"
+ ]
+ }
+ }
+ },
+ "info": {
+ "title": "Network Slice Controller (NSC) API",
+ "version": "1.0",
+ "description": "API for orchestrating and realizing transport network slice requests"
+ },
+ "produces": [
+ "application/json"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "tags": [
+ {
+ "name": "tfs",
+ "description": "Operations related to transport network slices with TeraflowSDN (TFS) controller"
+ },
+ {
+ "name": "ixia",
+ "description": "Operations related to transport network slices with IXIA NEII"
+ },
+ {
+ "name": "E2E",
+ "description": "Operations related to transport network slices with E2E Orchestrator"
+ }
+ ],
+ "definitions": {
+ "SliceResponse": {
+ "properties": {
+ "success": {
+ "type": "boolean",
+ "description": "Indicates if the request was successful",
+ "example": true
+ },
+ "data": {
+ "$ref": "#/definitions/SliceData"
+ },
+ "error": {
+ "type": "string",
+ "description": "Error message if request failed"
+ }
+ },
+ "type": "object"
+ },
+ "SliceData": {
+ "properties": {
+ "slices": {
+ "type": "array",
+ "description": "List of slices",
+ "items": {
+ "$ref": "#/definitions/SliceDetails"
+ }
+ },
+ "setup_time": {
+ "type": "number",
+ "description": "Slice setup time in milliseconds",
+ "example": 12.57
+ }
+ },
+ "type": "object"
+ },
+ "SliceDetails": {
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Slice ID",
+ "example": "slice-service-11327140-7361-41b3-aa45-e84a7fb40be9"
+ },
+ "source": {
+ "type": "string",
+ "description": "Source IP",
+ "example": "10.60.11.3"
+ },
+ "destination": {
+ "type": "string",
+ "description": "Destination IP",
+ "example": "10.60.60.105"
+ },
+ "vlan": {
+ "type": "string",
+ "description": "VLAN ID",
+ "example": "100"
+ },
+ "requirements": {
+ "type": "array",
+ "description": "List of requirements for the slice",
+ "items": {
+ "$ref": "#/definitions/SliceRequirement"
+ }
+ }
+ },
+ "type": "object"
+ },
+ "SliceRequirement": {
+ "properties": {
+ "constraint_type": {
+ "type": "string",
+ "description": "Type of constraint",
+ "example": "one-way-bandwidth[kbps]"
+ },
+ "constraint_value": {
+ "type": "string",
+ "description": "Constraint value",
+ "example": "2000"
+ }
+ },
+ "type": "object"
+ },
+ "ddbb_model": {
+ "properties": {
+ "slice_id": {
+ "type": "string"
+ },
+ "intent": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/NetworkSliceService"
+ }
+ },
+ "controller": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "NetworkSliceService": {
+ "properties": {
+ "ietf-network-slice-service:network-slice-services": {
+ "$ref": "#/definitions/NetworkSliceServices"
+ }
+ },
+ "type": "object"
+ },
+ "NetworkSliceServices": {
+ "properties": {
+ "slo-sle-templates": {
+ "$ref": "#/definitions/SloSleTemplates"
+ },
+ "slice-service": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/SliceService"
+ }
+ }
+ },
+ "type": "object"
+ },
+ "SloSleTemplates": {
+ "properties": {
+ "slo-sle-template": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/SloSleTemplate"
+ }
+ }
+ },
+ "type": "object"
+ },
+ "SloSleTemplate": {
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "slo-policy": {
+ "$ref": "#/definitions/SloPolicy"
+ },
+ "sle-policy": {
+ "$ref": "#/definitions/SlePolicy"
+ }
+ },
+ "type": "object"
+ },
+ "SloPolicy": {
+ "properties": {
+ "metric-bound": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/MetricBound"
+ }
+ }
+ },
+ "type": "object"
+ },
+ "MetricBound": {
+ "properties": {
+ "metric-type": {
+ "type": "string"
+ },
+ "metric-unit": {
+ "type": "string"
+ },
+ "bound": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "SlePolicy": {
+ "properties": {
+ "security": {
+ "type": "string"
+ },
+ "isolation": {
+ "type": "string"
+ },
+ "path-constraints": {
+ "$ref": "#/definitions/PathConstraints"
+ }
+ },
+ "type": "object"
+ },
+ "PathConstraints": {
+ "properties": {
+ "service-functions": {
+ "type": "string"
+ },
+ "diversity": {
+ "$ref": "#/definitions/Diversity"
+ }
+ },
+ "type": "object"
+ },
+ "Diversity": {
+ "properties": {
+ "diversity-type": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "SliceService": {
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "service-tags": {
+ "$ref": "#/definitions/ServiceTags"
+ },
+ "slo-sle-policy": {
+ "$ref": "#/definitions/SloSlePolicy"
+ },
+ "status": {
+ "type": "string"
+ },
+ "sdps": {
+ "$ref": "#/definitions/Sdps"
+ },
+ "connection-groups": {
+ "$ref": "#/definitions/ConnectionGroups"
+ }
+ },
+ "type": "object"
+ },
+ "ServiceTags": {
+ "properties": {
+ "tag-type": {
+ "$ref": "#/definitions/TagType"
+ }
+ },
+ "type": "object"
+ },
+ "TagType": {
+ "properties": {
+ "tag-type": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "SloSlePolicy": {
+ "properties": {
+ "slo-sle-template": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "Sdps": {
+ "properties": {
+ "sdp": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Sdp"
+ }
+ }
+ },
+ "type": "object"
+ },
+ "Sdp": {
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "geo-location": {
+ "type": "string"
+ },
+ "node-id": {
+ "type": "string"
+ },
+ "sdp-ip-address": {
+ "type": "string"
+ },
+ "tp-ref": {
+ "type": "string"
+ },
+ "service-match-criteria": {
+ "$ref": "#/definitions/ServiceMatchCriteria"
+ },
+ "incoming-qos-policy": {
+ "type": "string"
+ },
+ "outgoing-qos-policy": {
+ "type": "string"
+ },
+ "sdp-peering": {
+ "$ref": "#/definitions/SdpPeering"
+ },
+ "ac-svc-ref": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "attachment-circuits": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/AttachmentCircuit"
+ }
+ },
+ "status": {
+ "type": "string"
+ },
+ "sdp-monitoring": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "ServiceMatchCriteria": {
+ "properties": {
+ "match-criterion": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/MatchCriterion"
+ }
+ }
+ },
+ "type": "object"
+ },
+ "MatchCriterion": {
+ "properties": {
+ "index": {
+ "type": "integer"
+ },
+ "match-type": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ },
+ "target-connection-group-id": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "SdpPeering": {
+ "properties": {
+ "peer-sap-id": {
+ "type": "string"
+ },
+ "protocols": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "AttachmentCircuit": {
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "ac-ipv4-address": {
+ "type": "string"
+ },
+ "ac-ipv4-prefix-length": {
+ "type": "integer"
+ },
+ "sdp-peering": {
+ "$ref": "#/definitions/SdpPeering"
+ },
+ "status": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "ConnectionGroups": {
+ "properties": {
+ "connection-group": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ConnectionGroup"
+ }
+ }
+ },
+ "type": "object"
+ },
+ "ConnectionGroup": {
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "connectivity-type": {
+ "type": "string"
+ },
+ "connectivity-construct": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ConnectivityConstruct"
+ }
+ },
+ "status": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "ConnectivityConstruct": {
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "a2a-sdp": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/A2ASdp"
+ }
+ }
+ },
+ "type": "object"
+ },
+ "A2ASdp": {
+ "properties": {
+ "sdp-id": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "responses": {
+ "ParseError": {
+ "description": "When a mask can't be parsed"
+ },
+ "MaskError": {
+ "description": "When any error occurs on mask"
+ }
+ }
+}
diff --git a/doc/bootstrap_phase.puml b/doc/bootstrap_phase.puml
new file mode 100644
index 0000000000000000000000000000000000000000..60d4e9322d14b9a7dabd2bad76e8dab342ec7507
--- /dev/null
+++ b/doc/bootstrap_phase.puml
@@ -0,0 +1,66 @@
+@startuml Bootstrap_Phase
+actor "IETF NS Controller\n(Spring Boot)" as IETFNS
+participant "SloSleTemplateBootstrapService" as BOOTSTRAP
+participant "ResourceSpecificationTemplateRegistry" as REGISTRY
+participant "RestconfClient" as RC
+participant "RESTCONF Provider\n(e.g. TerflowSDN)" as PROVIDER
+participant "TMFResourceSpecRepositoryImpl" as REPO
+participant "OpenSlice TMF Catalog API" as TMF
+
+IETFNS ->> BOOTSTRAP: ApplicationReady Event\n(CommandLineRunner.run)
+activate BOOTSTRAP
+
+== Step 1: Create Fixed Resource Specification Templates ==
+
+BOOTSTRAP ->> BOOTSTRAP: createSloSleSpecification()\n→ IETFSloSleTemplateSpec\n + SloSleTemplateAsJson (TEXT)
+BOOTSTRAP ->> BOOTSTRAP: createSliceServiceSpecification()\n→ IETFSliceServiceSpec\n + SliceServiceAsJson (TEXT)
+BOOTSTRAP ->> BOOTSTRAP: createNetworkSliceServicesSpecification()\n→ IETFNetworkSliceServicesSpec\n + SloSleTemplatesAsJsonArray (TEXT)\n + SliceServicesAsJsonArray (TEXT)
+
+== Step 2: Retrieve Dynamic Templates from RESTCONF Provider (optional) ==
+
+alt RESTCONF client available
+ BOOTSTRAP ->> RC: getSloSleTemplates()
+ activate RC
+ RC ->> PROVIDER: GET /restconf/data/ietf-network-slice-service:\nnetwork-slice-services/slo-sle-templates\nAccept: application/yang-data+json
+ activate PROVIDER
+ PROVIDER -->> RC: JSON (RFC 9543 slo-sle-template list)
+ deactivate PROVIDER
+ RC -->> BOOTSTRAP: JSON string
+ deactivate RC
+ BOOTSTRAP ->> BOOTSTRAP: Rfc9543JsonConverter.parseSloSleTemplates(json)\n→ List
+ BOOTSTRAP ->> BOOTSTRAP: mapper.toLogicalResourceSpec(template)\nfor each retrieved template
+else RESTCONF client not available
+ BOOTSTRAP ->> BOOTSTRAP: Skip provider retrieval (warn)
+end
+
+== Step 3: Register All Specifications in TMF Catalog ==
+
+loop for each LogicalResourceSpecification
+ BOOTSTRAP ->> REPO: createOrUpdateResourceSpecByNameCategoryVersion(spec)
+ activate REPO
+ REPO ->> TMF: POST /resourceCatalogManagement/v4/resourceSpecification\nCategory: ns.ietf.controllers.osl.etsi.org/v1alpha
+ activate TMF
+ TMF -->> REPO: LogicalResourceSpecification { uuid }
+ deactivate TMF
+ REPO -->> BOOTSTRAP: registered spec with UUID
+ deactivate REPO
+ BOOTSTRAP ->> REGISTRY: registerTemplate(spec.name, spec.uuid)
+ activate REGISTRY
+ REGISTRY -->> BOOTSTRAP: stored
+ deactivate REGISTRY
+end
+
+BOOTSTRAP -->> IETFNS: Bootstrap complete\n(N template IDs registered)
+deactivate BOOTSTRAP
+
+note over IETFNS
+ Controller is now ready to receive
+ CREATE / UPDATE / DELETE messages
+ on ActiveMQ queues:
+ CREATE/ns.ietf.controllers.osl.etsi.org/v1alpha/0.1.0
+ Spec names mapped in ResourceSpecificationTemplateRegistry:
+ IETFSloSleTemplateSpec → uuid-1
+ IETFSliceServiceSpec → uuid-2
+ IETFNetworkSliceServicesSpec → uuid-3
+end note
+@enduml
diff --git a/doc/resource_creation_lifecycle.puml b/doc/resource_creation_lifecycle.puml
new file mode 100644
index 0000000000000000000000000000000000000000..df4adab8a90f593b02e30cb6ef165f23297b8907
--- /dev/null
+++ b/doc/resource_creation_lifecycle.puml
@@ -0,0 +1,67 @@
+@startuml Resource_Creation_Lifecycle
+actor "OpenSlice OSOM" as OSOM
+participant "ActiveMQ Broker" as QUEUE
+participant "Apache Camel" as CAMEL
+participant "ResourceRepoService" as RRS
+participant "Rfc9543SliceService\nDeserializer" as DESER
+participant "RestconfConsumerService" as RCS
+participant "TerflowSDN\n(RESTCONF Server)" as TFSDN
+participant "CatalogClient\n(TMF API)" as CATALOG
+
+OSOM ->> QUEUE: POST CREATE message\nQueue: CREATE/.../v1alpha/0.1.0\nBody: {resourceId, jsonRequest, ...}
+activate QUEUE
+
+QUEUE ->> CAMEL: Route message
+activate CAMEL
+
+CAMEL ->> RRS: processResourceCreate(message)
+activate RRS
+
+RRS ->> RRS: Extract jsonRequest from\nresource characteristics
+RRS ->> RRS: Validate jsonRequest
+
+RRS ->> DESER: mapper.readValue(jsonRequest)
+activate DESER
+
+DESER ->> DESER: Map hyphenated fields to\nJava camelCase properties:\nservice-tags → serviceTags\nslo-sle-policy → sloSleTemplate\nconnection-groups → connectionGroups
+DESER -->> RRS: SliceService object
+deactivate DESER
+
+RRS ->> RCS: provisionSliceService(sliceService)
+activate RCS
+
+RCS ->> TFSDN: PUT /api/ns/v0/slice-service/\n[SliceService JSON]
+activate TFSDN
+TFSDN -->> RCS: Success: SliceService with ID
+deactivate TFSDN
+
+RCS -->> RRS: Provisioned SliceService
+deactivate RCS
+
+RRS ->> RRS: Update resource status:\ninfoMessage = "Successfully\nprovisioned: {serviceId}"\nhealthStatus = "Healthy"\nresourceStatus = AVAILABLE
+
+RRS ->> CATALOG: Update resource in TMF API\nresourceId, status, infoMessage
+activate CATALOG
+CATALOG -->> RRS: Resource updated
+deactivate CATALOG
+
+RRS -->> CAMEL: Completed
+deactivate RRS
+
+CAMEL -->> QUEUE: Route complete
+deactivate CAMEL
+QUEUE -->> OSOM: ACK
+deactivate QUEUE
+
+note over OSOM
+ Resource is now AVAILABLE in OpenSlice
+ and network slice is provisioned on TerflowSDN
+end note
+
+alt On Failure
+ RRS ->> RRS: Catch exception
+ RRS ->> RRS: Set healthStatus = "Unhealthy"\nresourceStatus = UNKNOWN\ninfoMessage = error details
+ RRS ->> CATALOG: Update with failure status
+ RRS -->> CAMEL: Failed
+end
+@enduml
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..75503634e3cde514275fd314e454ac01e35e31e6
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,394 @@
+
+ 4.0.0
+ org.etsi.osl
+ org.etsi.osl.controllers.ietf.ns
+ 0.0.9-SNAPSHOT
+ org.etsi.osl.controllers.ietf.ns
+ org.etsi.osl.controllers.ietf.ns
+
+
+
+ UTF-8
+ UTF-8
+ 3.2.2
+ 1.18.28
+ 2.1.0
+ 1.5.3.Final
+ 17
+ 4.0.0-RC2
+ 2.8.11
+ 2.0.0
+ apache_v2
+ 1.7.0
+ 1.7.0
+ 22.0.1
+ 1.4.0
+
+
+
+
+ gitlab-maven
+ https://labs.etsi.org/rep/api/v4/groups/260/-/packages/maven
+
+
+
+
+ gitlab-maven
+ ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/maven
+
+
+ gitlab-maven
+ ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/maven
+
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring.boot-version}
+ pom
+ import
+
+
+
+ org.apache.camel.springboot
+ camel-spring-boot-dependencies
+ ${camel.version}
+ pom
+ import
+
+
+
+ com.google.guava
+ guava
+ 32.0.0-jre
+
+
+ org.keycloak.bom
+ keycloak-adapter-bom
+ ${keycloak.version}
+ pom
+ import
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-resource-server
+
+
+
+ com.jayway.jsonpath
+ json-path
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+ org.springframework.security
+ spring-security-oauth2-client
+
+
+ org.springframework.security
+ spring-security-core
+
+
+ org.springframework.security
+ spring-security-web
+
+
+ org.springframework.security
+ spring-security-config
+
+
+
+
+ org.projectlombok
+ lombok
+ provided
+ ${lombok-version}
+
+
+ org.openapitools
+ jackson-databind-nullable
+ 0.2.6
+
+
+
+ org.keycloak
+ keycloak-spring-boot-starter
+
+
+ org.keycloak
+ keycloak-spring-security-adapter
+
+
+
+ org.keycloak
+ keycloak-admin-client
+ ${keycloak.version}
+
+
+
+
+ org.etsi.osl
+ org.etsi.osl.model.tmf
+ ${org.etsi.osl.model.tmf.version}
+
+
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ ${springdoc.version}
+
+
+ org.springdoc
+ springdoc-openapi-ui
+ ${springdoc.openapiui.version}
+
+
+ org.springdoc
+ springdoc-openapi-security
+ ${springdoc.security.version}
+
+
+
+ org.mapstruct
+ mapstruct
+ ${mapstruct.version}
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+
+ javax.annotation
+ javax.annotation-api
+ 1.3.2
+ compile
+
+
+ jakarta.validation
+ jakarta.validation-api
+ 3.0.2
+
+
+ org.jetbrains
+ annotations
+ 13.0
+ compile
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-activemq
+
+
+ org.apache.activemq
+ activemq-amqp
+ test
+
+
+ org.apache.qpid
+ proton-j
+
+
+
+
+ org.messaginghub
+ pooled-jms
+
+
+
+
+ org.apache.camel.springboot
+ camel-spring-boot-starter
+
+
+ org.apache.activemq
+ activemq-pool
+
+
+ org.apache.camel
+ camel-activemq
+
+
+ org.apache.activemq
+ activemq-broker
+
+
+
+
+ org.apache.camel.springboot
+ camel-service-starter
+
+
+
+ org.apache.camel.springboot
+ camel-http-starter
+
+
+ org.apache.camel
+ camel-jackson
+
+
+ org.apache.camel
+ camel-stream
+
+
+
+ dk.brics.automaton
+ automaton
+ 1.11-8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ test
+
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${java.version}
+ ${java.version}
+
+ -parameters
+
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+ org.projectlombok
+ lombok
+ ${lombok-version}
+
+
+
+ org.projectlombok
+ lombok-mapstruct-binding
+ 0.2.0
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ license-maven-plugin
+ ${maven-license-plugin.version}
+
+ false
+ ========================LICENSE_START=================================
+ =========================LICENSE_END==================================
+ *.json
+
+
+
+ generate-license-headers
+
+ update-file-header
+
+ process-sources
+
+ ${license.licenseName}
+
+
+
+
+ download-licenses
+
+ download-licenses
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot-version}
+
+
+
+ repackage
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot-version}
+
+ exec
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/IETFNSGCSpringBoot.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/IETFNSGCSpringBoot.java
new file mode 100644
index 0000000000000000000000000000000000000000..e23d6fa4481b3dd9bdc463737db4e3515045bf9f
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/IETFNSGCSpringBoot.java
@@ -0,0 +1,59 @@
+package org.etsi.osl.controllers.ietf.ns;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.ExitCodeGenerator;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.ComponentScan;
+
+
+/**
+
+ *
+ *
+ * @author ctranoris
+ *
+ */
+@SpringBootApplication
+@EntityScan(basePackages = {"org.etsi.osl.controllers.ietf.ns","org.etsi.osl.controllers.ietf.ns.api"})
+
+public class IETFNSGCSpringBoot implements CommandLineRunner {
+
+ private static ApplicationContext applicationContext;
+
+ private static final Logger logger = LoggerFactory.getLogger("org.etsi.osl.controllers.ietf.ns");
+
+
+ @Override
+ public void run(String... arg0) throws Exception {
+ if (arg0.length > 0 && arg0[0].equals("exitcode")) {
+ throw new ExitException();
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+
+ logger.info("=========== STARTING org.etsi.osl.controllers.ietf.ns ==============================");
+ applicationContext = new SpringApplication(IETFNSGCSpringBoot.class).run(args);
+
+
+ // for (String beanName : applicationContext.getBeanDefinitionNames()) {
+ // System.out.println(beanName);
+ // }
+ }
+
+ class ExitException extends RuntimeException implements ExitCodeGenerator {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public int getExitCode() {
+ return 10;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/CategoryConfigurationService.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/CategoryConfigurationService.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea9d8db9477870c2d0dda4dba6e34b1ceb5b7bce
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/CategoryConfigurationService.java
@@ -0,0 +1,131 @@
+package org.etsi.osl.controllers.ietf.ns.api;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Centralized service for managing category prefixes and related configurations.
+ *
+ * This service provides a single source of truth for all TMF category values used
+ * throughout the IETF NS controller application, ensuring consistency across:
+ * - EntityToLogicalResourceMapper
+ * - EntityToLogicalResourceSpecMapper
+ * - LogicalResourceToEntityMapper
+ * - SloSleTemplateBootstrapService
+ * - TMFResourceInventoryRepositoryImpl
+ * - And all other components that need category information
+ *
+ * Configuration is read from application.yaml:
+ * osl-ietf-ns-controller:
+ * category: ns.ietf.controllers.osl.etsi.org
+ *
+ */
+@Service
+@Slf4j
+@Getter
+public class CategoryConfigurationService {
+
+ /**
+ * Base category prefix from application configuration.
+ * Format: {domain}.{service}.osl.etsi.org
+ * Example: ns.ietf.controllers.osl.etsi.org
+ */
+ @Value("${osl-ietf-ns-controller.category:ns.ietf.controllers.osl.etsi.org/v1alpha}")
+ private String categoryPrefix;
+
+ /**
+ * API version from application configuration.
+ * Format: x.y.z or alphanumeric identifier
+ * Example: 0.1.0 or v1alpha
+ */
+ @Value("${osl-ietf-ns-controller.version:0.1.0}")
+ private String version;
+
+ /**
+ * Constructs the full category string for a given entity type.
+ *
+ * Format: {categoryPrefix}/{version}/{entityType}
+ * Example: ns.ietf.controllers.osl.etsi.org/0.1.0/SloSleTemplate
+ *
+ * @param entityType The entity type (e.g., "SloSleTemplate", "Network", "Device")
+ * @return Full category string
+ */
+ public String getCategoryForEntity(String entityType) {
+ if (entityType == null || entityType.isEmpty()) {
+ throw new IllegalArgumentException("Entity type must not be null or empty");
+ }
+ return String.format("%s/%s/%s", categoryPrefix, version, entityType);
+ }
+
+ /**
+ * Gets the base category prefix with version suffix for specification templates.
+ *
+ * Format: {categoryPrefix}/{version}
+ * Example: ns.ietf.controllers.osl.etsi.org/0.1.0
+ *
+ * Used by:
+ * - SloSleTemplateBootstrapService
+ * - EntityToLogicalResourceSpecMapper
+ *
+ * @return Category prefix with version suffix
+ */
+ public String getCategoryForSpecifications() {
+ return String.format("%s", categoryPrefix);
+ }
+
+ /**
+ * Gets the base category prefix without version suffix.
+ *
+ * Format: {categoryPrefix}
+ * Example: ns.ietf.controllers.osl.etsi.org
+ *
+ * Used by:
+ * - EntityToLogicalResourceMapper
+ * - LogicalResourceToEntityMapper
+ * - TMFResourceInventoryRepositoryImpl
+ *
+ * @return Base category prefix
+ */
+ public String getCategoryPrefix() {
+ return categoryPrefix;
+ }
+
+ /**
+ * Gets the category prefix with version suffix for resources.
+ *
+ * Format: {categoryPrefix}/{version}
+ * Example: ns.ietf.controllers.osl.etsi.org/0.1.0
+ *
+ * Used by:
+ * - TMFResourceInventoryRepositoryImpl
+ *
+ * @return Category prefix with version suffix
+ */
+ public String getCategoryPrefixWithVersion() {
+ return String.format("%s/%s", categoryPrefix, version);
+ }
+
+ /**
+ * Logs the current configuration for diagnostic purposes.
+ * Call during application startup to verify category configuration.
+ */
+ public void logConfiguration() {
+ log.info("=== IETF NS Controller Category Configuration ===");
+ log.info("Category Prefix: {}", categoryPrefix);
+ log.info("Version: {}", version);
+ log.info("Category for Specifications: {}", getCategoryForSpecifications());
+ log.info("Category Prefix with Version: {}", getCategoryPrefixWithVersion());
+ log.info("Example - Entity Category: {}", getCategoryForEntity("SloSleTemplate"));
+ log.info("=========================================");
+ }
+
+ @Override
+ public String toString() {
+ return "CategoryConfigurationService{" +
+ "categoryPrefix='" + categoryPrefix + '\'' +
+ ", version='" + version + '\'' +
+ '}';
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/PartnerRouteBuilder.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/PartnerRouteBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..cbcbb5741b957755a174f71bc549e78ae61850d9
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/PartnerRouteBuilder.java
@@ -0,0 +1,91 @@
+package org.etsi.osl.controllers.ietf.ns.api;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.camel.LoggingLevel;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.dataformat.JsonLibrary;
+import org.etsi.osl.controllers.ietf.ns.repository.impl.ResourceRepoService;
+import org.etsi.osl.tmf.ri639.model.Resource;
+import org.etsi.osl.tmf.ri639.model.ResourceCreate;
+import org.etsi.osl.tmf.ri639.model.ResourceUpdate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+
+@Configuration
+@Component
+public class PartnerRouteBuilder extends RouteBuilder {
+
+ private static final Logger logger = LoggerFactory.getLogger("org.etsi.osl.controllers.ietf.ns");
+
+ @Value("${spring.application.name}")
+ private String compname;
+
+
+
+ @Autowired
+ private CategoryConfigurationService categoryConfig;
+
+ @Autowired
+ ResourceRepoService resourceRepoService;
+
+ @Override
+ public void configure() throws Exception {
+
+
+ String EVENT_CREATE = "jms:queue:CREATE/"+ categoryConfig.getCategoryPrefixWithVersion();
+
+ String EVENT_UPDATE = "jms:queue:UPDATE/"+ categoryConfig.getCategoryPrefixWithVersion();
+
+ String EVENT_DELETE = "jms:queue:DELETE/"+ categoryConfig.getCategoryPrefixWithVersion();
+
+ from(EVENT_CREATE)
+ .log(LoggingLevel.INFO, log, EVENT_CREATE + " message received!")
+ .to("log:DEBUG?showBody=true&showHeaders=true").unmarshal()
+ .json(JsonLibrary.Jackson, ResourceCreate.class, true)
+ .bean( resourceRepoService, "createResource( ${headers}, ${body} )")
+ .marshal().json( JsonLibrary.Jackson)
+ .convertBodyTo( String.class );
+
+ from(EVENT_UPDATE)
+ .log(LoggingLevel.INFO, log, EVENT_UPDATE + " message received!")
+ .to("log:DEBUG?showBody=true&showHeaders=true").unmarshal()
+ .json(JsonLibrary.Jackson, ResourceUpdate.class, true)
+ .bean( resourceRepoService, "updateResource( ${headers},${body} )")
+ .marshal().json( JsonLibrary.Jackson)
+ .convertBodyTo( String.class );;
+
+ from(EVENT_DELETE)
+ .log(LoggingLevel.INFO, log, EVENT_DELETE + " message received!")
+ .to("log:DEBUG?showBody=true&showHeaders=true").unmarshal()
+ .json(JsonLibrary.Jackson, ResourceUpdate.class, true)
+ .bean( resourceRepoService, "deleteResource( ${headers}, ${body} )")
+ .marshal().json( JsonLibrary.Jackson)
+ .convertBodyTo( String.class );;
+
+
+ }
+
+ static T toJsonObj(String content, Class valueType) throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ return mapper.readValue(content, valueType);
+ }
+
+ static String toJsonString(Object object) throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ return mapper.writeValueAsString(object);
+ }
+
+
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/ResourceSpecificationTemplateRegistry.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/ResourceSpecificationTemplateRegistry.java
new file mode 100644
index 0000000000000000000000000000000000000000..637b64f44c076921e8610233db9cf66cec9b726b
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/ResourceSpecificationTemplateRegistry.java
@@ -0,0 +1,93 @@
+package org.etsi.osl.controllers.ietf.ns.api;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Registry that stores resource specification template IDs created during bootstrap.
+ * These template IDs are used when creating actual resource instances.
+ *
+ * Usage:
+ * - Bootstrap process registers template IDs by entity type name
+ * - Services retrieve template IDs when creating resource instances
+ * - Template IDs reference the structure/schema definitions in TMF catalog
+ */
+@Component
+@Slf4j
+public class ResourceSpecificationTemplateRegistry {
+
+ /** Spec name for the IETFSloSleTemplateSpec resource specification. */
+ public static final String SPEC_SLO_SLE_TEMPLATE = "IETFSloSleTemplateSpec";
+
+ /** Spec name for the IETFSliceServiceSpec resource specification. */
+ public static final String SPEC_SLICE_SERVICE = "IETFSliceServiceSpec";
+
+ /** Spec name for the IETFNetworkSliceServicesSpec resource specification. */
+ public static final String SPEC_NETWORK_SLICE_SERVICES = "IETFNetworkSliceServicesSpec";
+
+ private final Map templateIds = new ConcurrentHashMap<>();
+
+ /**
+ * Register a template ID for an entity type
+ *
+ * @param entityTypeName Simple class name
+ * @param templateId UUID of the registered resource specification template
+ */
+ public void registerTemplate(String entityTypeName, String templateId) {
+ log.info("Registering template ID for {}: {}", entityTypeName, templateId);
+ templateIds.put(entityTypeName, templateId);
+ }
+
+ /**
+ * Get the template ID for an entity type
+ *
+ * @param entityTypeName Simple class name (e.g., "Datacenter", "EdgeSite")
+ * @return Optional containing the template ID if registered
+ */
+ public Optional getTemplateId(String entityTypeName) {
+ return Optional.ofNullable(templateIds.get(entityTypeName));
+ }
+
+ /**
+ * Check if a template is registered for an entity type
+ *
+ * @param entityTypeName Simple class name
+ * @return true if template ID exists
+ */
+ public boolean hasTemplate(String entityTypeName) {
+ return templateIds.containsKey(entityTypeName);
+ }
+
+ /**
+ * Get all registered template IDs
+ *
+ * @return Immutable map of entity type names to template IDs
+ */
+ public Map getAllTemplates() {
+ return Map.copyOf(templateIds);
+ }
+
+ /**
+ * Clear all registered templates (useful for testing)
+ */
+ public void clear() {
+ log.warn("Clearing all registered template IDs");
+ templateIds.clear();
+ }
+
+ /**
+ * Get count of registered templates
+ */
+ public int getTemplateCount() {
+ return templateIds.size();
+ }
+
+ public boolean getSloSleTemplateById(boolean b) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/SloSleTemplateBootstrapService.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/SloSleTemplateBootstrapService.java
new file mode 100644
index 0000000000000000000000000000000000000000..5554016990611de4f11c80283f93e93b7f451650
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/SloSleTemplateBootstrapService.java
@@ -0,0 +1,517 @@
+package org.etsi.osl.controllers.ietf.ns.api;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.io.ClassPathResource;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.NetworkSliceServices;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.ServiceStatus;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.ServiceTag;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.SliceService;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.SloSleTemplate;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.AvailabilityType;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.MetricBound;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.ServiceIsolationType;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.ServiceSecurityType;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.ServiceSloMetricType;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.SlePolicy;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.SloPolicy;
+import org.etsi.osl.controllers.ietf.ns.api.restconf.RestconfClient;
+import org.etsi.osl.controllers.ietf.ns.api.restconf.RestconfException;
+import org.etsi.osl.controllers.ietf.ns.api.restconf.Rfc9543JsonConverter;
+import org.etsi.osl.controllers.ietf.ns.mappers.EntityToLogicalResourceMapper;
+import org.etsi.osl.controllers.ietf.ns.mappers.EntityToLogicalResourceSpecMapper;
+import org.etsi.osl.controllers.ietf.ns.repository.impl.TMFResourceInventoryRepositoryImpl;
+import org.etsi.osl.controllers.ietf.ns.repository.impl.TMFResourceSpecRepositoryImpl;
+import org.etsi.osl.tmf.common.model.Any;
+import org.etsi.osl.tmf.rcm634.model.LogicalResourceSpecification;
+import org.etsi.osl.tmf.rcm634.model.ResourceSpecificationCharacteristic;
+import org.etsi.osl.tmf.rcm634.model.ResourceSpecificationCharacteristicValue;
+import org.etsi.osl.tmf.rcm634.model.ResourceSpecificationCreate;
+import org.etsi.osl.tmf.ri639.model.LogicalResource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.stereotype.Component;
+import java.util.UUID;
+
+/**
+ * Bootstrap component that registers SLO/SLE template resource specifications at startup.
+ *
+ * This service creates and registers LogicalResourceSpecification templates for
+ * different SLO/SLE tiers that define the structure and schema
+ * for each tier in the TMF catalog.
+ *
+ *
+ * @author ctranoris
+ */
+@Component
+@Slf4j
+public class SloSleTemplateBootstrapService implements CommandLineRunner {
+
+ @Autowired
+ private TMFResourceSpecRepositoryImpl tmfRepository;
+
+ @Autowired
+ private EntityToLogicalResourceSpecMapper mapper;
+
+ @Autowired
+ private EntityToLogicalResourceMapper logicalResourceMapper;
+
+ @Autowired
+ private ResourceSpecificationTemplateRegistry templateRegistry;
+
+ @Autowired(required = false)
+ private RestconfClient restconfClient;
+
+ @Autowired
+ private CategoryConfigurationService categoryConfig;
+
+
+ @Autowired
+ private TMFResourceInventoryRepositoryImpl tmfResourceinventory;
+
+
+ // Container to store provider templates for later slice service creation
+ private List providerTemplates = new ArrayList<>();
+
+ /**
+ * Executed at application startup via CommandLineRunner interface.
+ * Creates default templates, retrieves templates from RESTCONF provider,
+ * and registers all templates in the TMF catalog.
+ */
+ @Override
+ public void run(String... args) throws Exception {
+ log.info("=== Starting SLO/SLE Template Resource Specification Bootstrap ===");
+
+ try {
+ // Create list to store all specifications (default + retrieved)
+ List allSpecifications = createSpecificationTemplates();
+
+
+ // Retrieve templates from RESTCONF provider
+ if (restconfClient != null) {
+ log.info("Step 1: Retrieving SLO/SLE templates from RESTCONF provider");
+ List retrievedSpecs = retrieveTemplatesFromProvider();
+ allSpecifications.addAll(retrievedSpecs);
+ log.info(" Retrieved {} templates from provider", retrievedSpecs.size());
+ } else {
+ log.warn("Step 2: RESTCONF client not available - skipping provider template retrieval");
+ }
+
+ // 3. Register all default and retrieved specifications
+ log.info("Step 3: Registering all SLO/SLE templates in TMF catalog");
+ for (LogicalResourceSpecification spec : allSpecifications) {
+ registerSpecification(spec);
+ }
+
+ log.info("=== SLO/SLE Template Bootstrap Complete: {} template IDs registered ===",
+ templateRegistry.getTemplateCount());
+
+ // Log all registered template IDs
+ templateRegistry.getAllTemplates().forEach((templateName, templateId) ->
+ log.info(" {} -> {}", templateName, templateId));
+
+ } catch (Exception e) {
+ log.error("Error during SLO/SLE template bootstrap initialization", e);
+ // Don't throw - allow application to start even if bootstrap fails
+ }
+ }
+
+ /**
+ * Create template specifications for all entity types
+ */
+ private List createSpecificationTemplates() {
+ log.info("Creating resource specification templates for all entity types");
+ List specs = new ArrayList<>();
+ specs.add(createSloSleSpecification());
+ specs.add(createSliceServiceSpecification());
+ specs.add(createNetworkSliceServicesSpecification());
+ return specs;
+ }
+
+
+
+ private LogicalResourceSpecification createSloSleSpecification() {
+ log.info("Creating default SloSleTemplate resource specification");
+
+ // Build a representative SloSleTemplate whose fields will be reflected
+ // as ResourceSpecificationCharacteristic entries by the mapper
+ SloSleTemplate template = new SloSleTemplate();
+ template.setId("SloSleTemplate");
+ template.setDescription(
+ "IETF RFC 9543 SLO/SLE template defining service level objectives and expectations");
+
+ // SloPolicy — representative defaults covering the three main metric families
+ SloPolicy sloPolicy = new SloPolicy();
+ sloPolicy.setAvailability(new AvailabilityType(99.9, "per-month", 43L));
+ sloPolicy.setMtu(1500L);
+ sloPolicy.addMetricBound(
+ new MetricBound(ServiceSloMetricType.ONE_WAY_DELAY_MAXIMUM, "ms", 50L));
+ sloPolicy.addMetricBound(
+ new MetricBound(ServiceSloMetricType.TWO_WAY_BANDWIDTH, "Mbps", 1000L));
+ sloPolicy.addMetricBound(
+ new MetricBound(ServiceSloMetricType.ONE_WAY_PACKET_LOSS, "%", 1L));
+ template.setSloPolicy(sloPolicy);
+
+ // SlePolicy — representative defaults for security and isolation requirements
+ SlePolicy slePolicy = new SlePolicy();
+ slePolicy.addSecurityRequirement(ServiceSecurityType.ENCRYPTION_REQUIRED);
+ slePolicy.addIsolationRequirement(ServiceIsolationType.LOGICAL_ISOLATION);
+ slePolicy.setMaxOccupancyLevel((short) 100);
+ template.setSlePolicy(slePolicy);
+
+ // Convert fields to ResourceSpecificationCharacteristic entries via mapper
+ LogicalResourceSpecification spec = mapper.toLogicalResourceSpec(template);
+
+ spec.setName(ResourceSpecificationTemplateRegistry.SPEC_SLO_SLE_TEMPLATE);
+ spec.setCategory(categoryConfig.getCategoryForSpecifications());
+ spec.setVersion(categoryConfig.getVersion());
+ spec.setDescription(
+ "IETF RFC 9543 SLO/SLE template specification for network slice service level objectives and expectations");
+
+ // Carry the full template as a JSON blob so consumers can round-trip
+ // the original RFC 9543 structure without re-assembling it from individual characteristics
+ ResourceSpecificationCharacteristic jsonCharacteristic = new ResourceSpecificationCharacteristic();
+ jsonCharacteristic.setName("SloSleTemplateAsJson");
+ jsonCharacteristic.setValueType("TEXT");
+ jsonCharacteristic.setConfigurable(true);
+ ResourceSpecificationCharacteristicValue jsonCharValue = new ResourceSpecificationCharacteristicValue();
+ jsonCharValue.setIsDefault(true);
+ jsonCharValue.setValueType("TEXT");
+ Any jsonAny = new Any();
+ jsonAny.setValue("");
+ jsonAny.setAlias("SloSleTemplateAsJson");
+ jsonCharValue.setValue(jsonAny);
+ jsonCharacteristic.addResourceSpecCharacteristicValueItem(jsonCharValue);
+ spec.addResourceSpecCharacteristicItem(jsonCharacteristic);
+
+ log.info("Created SloSleTemplate specification: name={}, category={}, version={}",
+ spec.getName(), spec.getCategory(), spec.getVersion());
+ return spec;
+ }
+
+ /**
+ * Creates a LogicalResourceSpecification for an IETF RFC 9543 Network Slice Service.
+ *
+ * The specification captures the structural schema of a SliceService — its identity,
+ * operational status, SLO/SLE template reference, SDPs, connection groups and service
+ * tags — as TMF ResourceSpecificationCharacteristic entries.
+ * A configurable {@code SliceServiceAsJson} TEXT characteristic is also added so that
+ * consumers can store and round-trip the full RFC 9543 JSON payload without having to
+ * reassemble it from individual characteristics.
+ *
+ * @return LogicalResourceSpecification representing a Network Slice Service schema
+ */
+ private LogicalResourceSpecification createSliceServiceSpecification() {
+ log.info("Creating NetworkSliceService resource specification");
+
+ LogicalResourceSpecification spec = new LogicalResourceSpecification();
+ spec.setName(ResourceSpecificationTemplateRegistry.SPEC_SLICE_SERVICE);
+ spec.setCategory(categoryConfig.getCategoryForSpecifications());
+ spec.setVersion(categoryConfig.getVersion());
+ spec.setDescription(
+ "IETF RFC 9543 Network Slice Service specification defining connectivity endpoints, "
+ + "SLO/SLE policy reference, and operational status");
+ spec.setLifecycleStatus("Active");
+
+ // Core identity
+ addSpecCharacteristic(spec, "id", "TEXT", "", true);
+ addSpecCharacteristic(spec, "description", "TEXT", "", true);
+ addSpecCharacteristic(spec, "testOnly", "BOOLEAN", "false", true);
+
+ // Operational status (RFC 9543 admin/oper states)
+ addSpecCharacteristic(spec, "status.adminStatus", "TEXT", "admin-up", true);
+ addSpecCharacteristic(spec, "status.operStatus", "TEXT", "operational", true);
+
+ // SLO/SLE template reference (stores the referenced SloSleTemplate ID)
+ addSpecCharacteristic(spec, "sloSleTemplate", "TEXT", "", false);
+
+ // Structural collections
+ addSpecCharacteristic(spec, "serviceTags", "ARRAY", "[]", true);
+ addSpecCharacteristic(spec, "sdps", "ARRAY", "[]", true);
+ addSpecCharacteristic(spec, "connectionGroups", "ARRAY", "[]", true);
+
+ // Full RFC 9543 JSON blob — configurable at instantiation time
+ addSpecCharacteristic(spec, "SliceServiceAsJson", "TEXT", "", true);
+
+ log.info("Created NetworkSliceService specification: name={}, category={}, version={}",
+ spec.getName(), spec.getCategory(), spec.getVersion());
+ return spec;
+ }
+
+ /**
+ * Creates a LogicalResourceSpecification for the IETF RFC 9543 NetworkSliceServices container.
+ *
+ * This specification carries the entire RFC 9543 data model as two JSON array
+ * characteristics, allowing consumers to read and write the full container content
+ * without navigating individual nested characteristics:
+ *
+ *
{@code SloSleTemplatesAsJsonArray} – JSON array of all slo-sle-template entries
+ *
{@code SliceServicesAsJsonArray} – JSON array of all slice-service entries
+ *
+ *
+ * @return LogicalResourceSpecification representing the NetworkSliceServices container schema
+ */
+ private LogicalResourceSpecification createNetworkSliceServicesSpecification() {
+ log.info("Creating NetworkSliceServices resource specification");
+
+ LogicalResourceSpecification spec = new LogicalResourceSpecification();
+ spec.setName(ResourceSpecificationTemplateRegistry.SPEC_NETWORK_SLICE_SERVICES);
+ spec.setCategory(categoryConfig.getCategoryForSpecifications());
+ spec.setVersion(categoryConfig.getVersion());
+ spec.setDescription(
+ "IETF RFC 9543 top-level network-slice-services container specification "
+ + "holding SLO/SLE templates and slice services as JSON arrays");
+ spec.setLifecycleStatus("Active");
+
+ addSpecCharacteristic(spec, "SloSleTemplatesAsJsonArray", "TEXT", "[]", true);
+ addSpecCharacteristic(spec, "SliceServicesAsJsonArray", "TEXT", "[]", true);
+
+ log.info("Created NetworkSliceServices specification: name={}, category={}, version={}",
+ spec.getName(), spec.getCategory(), spec.getVersion());
+ return spec;
+ }
+
+ /**
+ * Adds a single ResourceSpecificationCharacteristic to the given spec.
+ *
+ * @param spec target specification
+ * @param name characteristic name
+ * @param valueType TMF value type (TEXT, BOOLEAN, NUMBER, ARRAY, …)
+ * @param defaultValue string representation of the default value
+ * @param configurable whether the characteristic is writable by consumers
+ */
+ private void addSpecCharacteristic(LogicalResourceSpecification spec,
+ String name, String valueType, String defaultValue, boolean configurable) {
+ ResourceSpecificationCharacteristic characteristic = new ResourceSpecificationCharacteristic();
+ characteristic.setName(name);
+ characteristic.setValueType(valueType);
+ characteristic.setConfigurable(configurable);
+
+ ResourceSpecificationCharacteristicValue charValue = new ResourceSpecificationCharacteristicValue();
+ charValue.setIsDefault(true);
+ charValue.setValueType(valueType);
+
+ Any anyValue = new Any();
+ anyValue.setValue(defaultValue);
+ anyValue.setAlias(name);
+ charValue.setValue(anyValue);
+
+ characteristic.addResourceSpecCharacteristicValueItem(charValue);
+ spec.addResourceSpecCharacteristicItem(characteristic);
+ }
+
+ /**
+ * Retrieve RFC 9543 SLO/SLE templates directly from the RESTCONF provider.
+ *
+ * This method queries the RESTCONF provider for all SLO/SLE templates at the
+ * /restconf/data/ietf-network-slice-service:network-slice-services/slo-sle-templates
+ * endpoint and converts them to LogicalResourceSpecification for registration
+ * in the TMF catalog.
+ *
+ * Also stores the raw SloSleTemplate objects for later use in creating example
+ * slice services that reference these templates.
+ *
+ * @return List of LogicalResourceSpecification objects extracted from provider
+ */
+ private List retrieveTemplatesFromProvider() {
+ List retrievedSpecs = new ArrayList<>();
+ Set processedTemplateIds = new HashSet<>(); // Avoid duplicates
+ providerTemplates.clear(); // Clear any previous templates
+
+ try {
+ log.debug("Querying RESTCONF provider for SLO/SLE templates at /slo-sle-templates endpoint");
+
+ // Get the templates response as JSON string
+ String templatesJson = getTemplatesFromProvider();
+
+ if (templatesJson == null || templatesJson.isEmpty()) {
+ log.info("No SLO/SLE templates retrieved from provider");
+ return retrievedSpecs;
+ }
+
+ // Parse the RFC 9543 formatted response using Rfc9543JsonConverter
+ List templates = Rfc9543JsonConverter.parseSloSleTemplates(templatesJson);
+ log.info("Retrieved {} SLO/SLE templates from provider", templates.size());
+
+ // Convert each template to LogicalResourceSpecification
+ for (SloSleTemplate template : templates) {
+ if (template != null && template.getId() != null) {
+ String templateId = template.getId();
+
+ // Process each template only once
+ if (!processedTemplateIds.contains(templateId)) {
+ processedTemplateIds.add(templateId);
+ log.debug("Processing SloSleTemplate from provider: {}", templateId);
+
+ // Store the raw template for later slice service creation
+ providerTemplates.add(template);
+
+ // Convert to LogicalResourceSpecification
+ LogicalResourceSpecification spec = mapper.toLogicalResourceSpec(template);
+
+ //Override Name, to show NSC templates as specnames...
+ spec.setName( ResourceSpecificationTemplateRegistry.SPEC_SLO_SLE_TEMPLATE +"_" + template.getEntityName() );
+
+ spec.setCategory(categoryConfig.getCategoryForSpecifications());
+ spec.setVersion(categoryConfig.getVersion());
+ spec.setDescription("SLO/SLE template '" + templateId + "' retrieved from RESTCONF provider");
+
+ retrievedSpecs.add(spec);
+ log.info("Extracted SloSleTemplate specification: {}", templateId);
+ }
+ }
+ }
+
+ log.info("Successfully extracted {} unique SloSleTemplate specifications from provider",
+ retrievedSpecs.size());
+
+ } catch (RestconfException e) {
+ log.error("RESTCONF error while retrieving templates from provider: {} - {} ({})",
+ e.getMessage(), e.getErrorType(), e.getErrorTag(), e);
+ } catch (Exception e) {
+ log.error("Error retrieving templates from RESTCONF provider", e);
+ // Continue with bootstrap even if provider retrieval fails
+ }
+
+ return retrievedSpecs;
+ }
+
+ /**
+ * Retrieves raw JSON response for SLO/SLE templates from the RESTCONF provider.
+ *
+ * Makes a request via RestconfClient to the provider's
+ * /restconf/data/ietf-network-slice-service:network-slice-services/slo-sle-templates
+ * endpoint to retrieve RFC 9543 formatted templates with proper authentication.
+ *
+ * @return JSON string containing the RFC 9543 formatted templates response
+ * @throws RestconfException if the retrieval fails
+ */
+ private String getTemplatesFromProvider() throws RestconfException {
+ try {
+ log.debug("Retrieving SLO/SLE templates from RESTCONF provider via RestconfClient");
+
+ // Use RestconfClient.getSloSleTemplates() which handles:
+ // - HTTP Basic Authentication (admin/admin123)
+ // - Proper RESTCONF headers (application/yang-data+json)
+ // - Error handling and retries
+ // - Logging at each stage
+ String templatesJson = restconfClient.getSloSleTemplates();
+
+ if (templatesJson == null || templatesJson.isEmpty() || "{}".equals(templatesJson)) {
+ log.debug("No templates retrieved from provider - provider may not have templates available");
+ return null;
+ }
+
+ log.debug("Successfully retrieved SLO/SLE templates from provider");
+ return templatesJson;
+
+ } catch (RestconfException e) {
+ log.warn("Failed to retrieve templates from RESTCONF provider: {}", e.getMessage());
+ // Return null to allow bootstrap to continue with default templates
+ return null;
+ } catch (Exception e) {
+ log.error("Unexpected error retrieving templates from provider", e);
+ // Return null to allow bootstrap to continue with default templates
+ return null;
+ }
+ }
+
+
+
+
+ /**
+ * Register a specification using createOrUpdateResourceSpecByNameCategoryVersion
+ */
+ private void registerSpecification(LogicalResourceSpecification spec) {
+ log.info("Registering specification: {} (category: {}, version: {})",
+ spec.getName(), spec.getCategory(), spec.getVersion());
+
+ try {
+ // Convert LogicalResourceSpecification to ResourceSpecificationCreate
+ ResourceSpecificationCreate createSpec = new ResourceSpecificationCreate();
+ createSpec.setName(spec.getName());
+ createSpec.setCategory(spec.getCategory());
+ createSpec.setVersion(spec.getVersion());
+ createSpec.setDescription(spec.getDescription());
+ createSpec.setLifecycleStatus(spec.getLifecycleStatus());
+ createSpec.setType("LogicalResourceSpecification");
+ createSpec.setBaseType("ResourceSpecification");
+
+ // Copy characteristics and relationships (convert Set to List)
+ if (spec.getResourceSpecCharacteristic() != null) {
+ createSpec.setResourceSpecificationCharacteristic(
+ new ArrayList<>(spec.getResourceSpecCharacteristic()));
+ }
+ if (spec.getResourceSpecRelationship() != null) {
+ createSpec.setResourceSpecificationRelationship(
+ new ArrayList<>(spec.getResourceSpecRelationship()));
+ }
+
+ // Register the specification
+ LogicalResourceSpecification registered =
+ tmfRepository.createOrUpdateResourceSpecByNameCategoryVersion(createSpec);
+
+ if (registered != null && registered.getUuid() != null) {
+ log.info("Successfully registered specification: {} with ID: {}",
+ spec.getName(), registered.getUuid());
+
+ // Store the template ID in the registry for future use
+ templateRegistry.registerTemplate(spec.getName(), registered.getUuid());
+ } else {
+ log.warn("Failed to register specification: {}", spec.getName());
+ }
+
+ } catch (Exception e) {
+ log.error("Error registering specification: {}", spec.getName(), e);
+ }
+ }
+
+ /**
+ * Register a LogicalResource (slice service) in the TMF Resource Inventory.
+ *
+ * Slice services are registered as LogicalResource instances in the TMF639
+ * Resource Inventory for use in resource management and monitoring.
+ *
+ * @param resource LogicalResource instance representing a slice service
+ */
+ private void registerLogicalResource(LogicalResource resource) {
+ log.info("Registering LogicalResource: {} (category: {})",
+ resource.getName(), resource.getCategory());
+
+ try {
+ if (resource == null || resource.getName() == null) {
+ log.warn("Cannot register null or invalid LogicalResource");
+ return;
+ }
+
+ // In a real implementation, this would call the TMF Resource Inventory API
+ // For now, we log the resource for demonstration purposes
+ log.info("LogicalResource ready for inventory storage: name={}, uuid={}, status={}",
+ resource.getName(),
+ resource.getUuid(),
+ resource.getResourceStatus());
+
+ // TODO: Integrate with actual Resource Inventory Management service
+ // tmfRepository.createOrUpdateLogicalResource(resource);
+ tmfResourceinventory.createOrUpdate(resource);
+
+ } catch (Exception e) {
+ log.error("Error registering LogicalResource: {}", resource.getName(), e);
+ }
+ }
+
+
+
+
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/config/ActiveMQComponentConfig.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/config/ActiveMQComponentConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..54a7454f95554e2725313611f6c23386f0937ecc
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/config/ActiveMQComponentConfig.java
@@ -0,0 +1,22 @@
+package org.etsi.osl.controllers.ietf.ns.api.config;
+
+import org.apache.camel.component.activemq.ActiveMQComponent;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import jakarta.jms.ConnectionFactory;
+
+/**
+ * @author ctranoris
+ *
+ */
+@Configuration
+public class ActiveMQComponentConfig {
+
+ @Bean(name = "activemq")
+ public ActiveMQComponent createComponent(ConnectionFactory factory) {
+ ActiveMQComponent activeMQComponent = new ActiveMQComponent();
+ activeMQComponent.setConnectionFactory(factory);
+ return activeMQComponent;
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/AttachmentCircuit.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/AttachmentCircuit.java
new file mode 100644
index 0000000000000000000000000000000000000000..4022325969e892c342b4284c060c80ebad341ac5
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/AttachmentCircuit.java
@@ -0,0 +1,19 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents an attachment circuit for service demarcation points.
+ * Includes ingress and egress QoS policies.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class AttachmentCircuit {
+ private String id;
+ private String description;
+ private QosPolicy ingressPolicy;
+ private QosPolicy egressPolicy;
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ConnectionGroup.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ConnectionGroup.java
new file mode 100644
index 0000000000000000000000000000000000000000..87b7a90cb9c49958fd40563ecd5df3657212cf55
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ConnectionGroup.java
@@ -0,0 +1,27 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents a logical grouping of connectivity constructs.
+ * Defines the connectivity fabric per RFC 9543 with support for
+ * P2P (Point-to-Point), P2MP (Point-to-Multipoint), and A2A (Any-to-Any) patterns.
+ * Each group can apply a policy override relative to the SliceService-level SLO/SLE template.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ConnectionGroup {
+ private String id;
+ private ConnectivityType connectivityType;
+
+ // 0-to-1 relationship: optional SLO/SLE policy override at the group level
+ private PolicyRef policyOverride;
+
+ // 1-to-many relationship: contains multiple connectivity constructs
+ private List connectivityConstructs = new ArrayList<>();
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ConnectivityConstruct.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ConnectivityConstruct.java
new file mode 100644
index 0000000000000000000000000000000000000000..d50ecd115d46f6dae8da798de8b58d6ff94a9e06
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ConnectivityConstruct.java
@@ -0,0 +1,34 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents a connectivity construct within a connection group.
+ * Supports P2P (p2p-sender-sdp / p2p-receiver-sdp), P2MP, and A2A patterns.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class ConnectivityConstruct {
+ private String id;
+
+ /** P2P sender SDP reference (RFC 9543: p2p-sender-sdp). */
+ @JsonProperty("p2p-sender-sdp")
+ private String p2pSenderSdp;
+
+ /** P2P receiver SDP reference (RFC 9543: p2p-receiver-sdp). */
+ @JsonProperty("p2p-receiver-sdp")
+ private String p2pReceiverSdp;
+
+ private ConnectivityType type;
+ private ConstructStatus status;
+ private PolicyRef policyOverride;
+ private List members = new ArrayList<>();
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ConnectivityType.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ConnectivityType.java
new file mode 100644
index 0000000000000000000000000000000000000000..f3dec27dac435071b5fd99196c9f72540552e7fc
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ConnectivityType.java
@@ -0,0 +1,33 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Enumeration for connectivity types as per IETF Network Slice Service specification.
+ * Accepts RFC 9543 kebab-case strings (e.g., "point-to-point") and UPPER_SNAKE_CASE.
+ */
+public enum ConnectivityType {
+ P2P,
+ P2MP,
+ A2A;
+
+ @JsonCreator
+ public static ConnectivityType fromValue(String value) {
+ if (value == null || value.isBlank()) return null;
+ String lower = value.toLowerCase();
+ if (lower.contains("point-to-point") || lower.equals("p2p")) return P2P;
+ if (lower.contains("point-to-multipoint") || lower.equals("p2mp")) return P2MP;
+ if (lower.contains("any-to-any") || lower.equals("a2a")) return A2A;
+ try { return valueOf(value.toUpperCase()); } catch (IllegalArgumentException e) { return null; }
+ }
+
+ @JsonValue
+ public String toValue() {
+ return switch (this) {
+ case P2P -> "point-to-point";
+ case P2MP -> "point-to-multipoint";
+ case A2A -> "any-to-any";
+ };
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ConstructStatus.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ConstructStatus.java
new file mode 100644
index 0000000000000000000000000000000000000000..5bf1599d759283795c088623687e3af35e92dc26
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ConstructStatus.java
@@ -0,0 +1,17 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents the status of a connectivity construct.
+ * Includes operational state and reason for the current state.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ConstructStatus {
+ private String state;
+ private String reason;
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/MatchCriterion.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/MatchCriterion.java
new file mode 100644
index 0000000000000000000000000000000000000000..c064745d44d711dfeb2288a8380a34adb670a763
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/MatchCriterion.java
@@ -0,0 +1,22 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents a match criterion for classifying traffic in service demarcation points.
+ * Used to identify and match traffic based on various attributes such as interface,
+ * VLAN, IP prefix, or MPLS label.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class MatchCriterion {
+ private String id;
+ private MatchType matchType;
+ private String ifName;
+ private Integer vlanId;
+ private String ipPrefix;
+ private Long mplsLabel;
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/MatchType.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/MatchType.java
new file mode 100644
index 0000000000000000000000000000000000000000..01e4dcf33028079759c14601dbdd17d2388469fe
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/MatchType.java
@@ -0,0 +1,12 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+/**
+ * Enumeration for match criteria types as per IETF Network Slice Service specification.
+ */
+public enum MatchType {
+ mt_any,
+ mt_interface,
+ mt_vlan,
+ mt_ip_prefix,
+ mt_mpls_label
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/NetworkSliceServices.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/NetworkSliceServices.java
new file mode 100644
index 0000000000000000000000000000000000000000..d36e9a8e3d743b1c4f0c40b929830a3218f5bb72
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/NetworkSliceServices.java
@@ -0,0 +1,106 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.etsi.osl.controllers.ietf.ns.domain.common.LogicalResourceMappable;
+import org.etsi.osl.tmf.ri639.model.LogicalResource;
+import org.etsi.osl.tmf.ri639.model.ResourceAdministrativeStateType;
+import org.etsi.osl.tmf.ri639.model.ResourceOperationalStateType;
+import org.etsi.osl.tmf.ri639.model.ResourceStatusType;
+
+/**
+ * Root container for IETF Network Slice Services.
+ * Represents the top-level container for managing network slice services and their
+ * associated SLO/SLE templates as per draft-ietf-teas-ietf-network-slice-nbi-yang-25.
+ *
+ * Implements LogicalResourceMappable to enable conversion to TMF LogicalResource instances.
+ * The jsonRequest field contains the RFC 9543 formatted JSON with slice-service array.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class NetworkSliceServices implements LogicalResourceMappable {
+
+
+ // 1-to-many relationship: contains multiple SLO/SLE templates
+ private List sloSleTemplates = new ArrayList<>();
+
+ // 1-to-many relationship: contains multiple slice services
+ private List sliceServices = new ArrayList<>();
+
+ // ========== LogicalResourceMappable Implementation ==========
+
+ /**
+ * Get the unique identifier - uses the first slice service ID if available.
+ */
+ @Override
+ @JsonIgnore
+ public String getEntityId() {
+ if (sliceServices != null && !sliceServices.isEmpty()) {
+ return sliceServices.get(0).getId();
+ }
+ return "unknown";
+ }
+
+ @Override
+ @JsonIgnore
+ public String getEntityName() {
+ if (sliceServices != null && !sliceServices.isEmpty()) {
+ return sliceServices.get(0).getId();
+ }
+ return "Network Slice Services";
+ }
+
+ @Override
+ @JsonIgnore
+ public String getEntityDescription() {
+ if (sliceServices != null && !sliceServices.isEmpty() && sliceServices.get(0).getDescription() != null) {
+ return sliceServices.get(0).getDescription();
+ }
+ return "IETF RFC 9543 Network Slice Services";
+ }
+
+ @Override
+ @JsonIgnore
+ public boolean hasStatusMapping() {
+ return sliceServices != null && !sliceServices.isEmpty() && sliceServices.get(0).getStatus() != null;
+ }
+
+ @Override
+ @JsonIgnore
+ public void mapStatusToResourceStates(LogicalResource resource) {
+ if (sliceServices != null && !sliceServices.isEmpty()) {
+ SliceService firstService = sliceServices.get(0);
+ if (firstService.getStatus() != null) {
+ ServiceStatus status = firstService.getStatus();
+
+ // Map administrative state
+ String adminState = status.getAdminStatus();
+ if (adminState != null && adminState.equals("admin-up")) {
+ resource.setAdministrativeState(ResourceAdministrativeStateType.UNLOCKED);
+ } else if (adminState != null && adminState.equals("admin-down")) {
+ resource.setAdministrativeState(ResourceAdministrativeStateType.LOCKED);
+ }
+
+ // Map operational state
+ String operState = status.getOperStatus();
+ if (operState != null && operState.equals("operational")) {
+ resource.setOperationalState(ResourceOperationalStateType.ENABLE);
+ } else if (operState != null && operState.equals("non-operational")) {
+ resource.setOperationalState(ResourceOperationalStateType.DISABLE);
+ }
+
+ // Set resource status (active if operational, disabled otherwise)
+ if ("operational".equals(operState)) {
+ resource.setResourceStatus(ResourceStatusType.AVAILABLE);
+ } else {
+ resource.setResourceStatus(ResourceStatusType.SUSPENDED);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/PolicyRef.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/PolicyRef.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3b1fb167293aa46ef0cc780b4ab4ed544462e8c
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/PolicyRef.java
@@ -0,0 +1,17 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents a reference to an SLO/SLE policy template.
+ * Used to override default SLO/SLE policies at ConnectionGroup or ConnectivityConstruct levels.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class PolicyRef {
+ private String templateId;
+ private String note;
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/QosPolicy.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/QosPolicy.java
new file mode 100644
index 0000000000000000000000000000000000000000..14498f9efa68f4d2adbf090800ae1b68bf8565dd
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/QosPolicy.java
@@ -0,0 +1,20 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents Quality of Service (QoS) policy parameters.
+ * Includes class selector, committed information rate (CIR), peak information rate (PIR),
+ * and peak burst size (PBS).
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class QosPolicy {
+ private String classSelector;
+ private String cir; // bandwidth
+ private String pir; // bandwidth
+ private String pbs; // size
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/Rfc9543SliceServiceDeserializer.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/Rfc9543SliceServiceDeserializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..8bfbb908ac6adc35ffbb014f64c824f9b9c82f93
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/Rfc9543SliceServiceDeserializer.java
@@ -0,0 +1,174 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Custom Jackson deserializer for RFC 9543 formatted SliceService JSON.
+ *
+ * Maps RFC 9543 hyphenated field names to Java camelCase property names:
+ * - "service-tags" → serviceTags
+ * - "slo-sle-template" → sloSleTemplate (direct string id or nested object)
+ * - "connection-groups" → connectionGroups
+ * - "connectivity-construct" → connectivityConstructs
+ * - "p2p-sender-sdp" → p2pSenderSdp in ConnectivityConstruct
+ * - "p2p-receiver-sdp" → p2pReceiverSdp in ConnectivityConstruct
+ */
+public class Rfc9543SliceServiceDeserializer extends StdDeserializer {
+
+ private static final Logger logger = LoggerFactory.getLogger(Rfc9543SliceServiceDeserializer.class);
+
+ public Rfc9543SliceServiceDeserializer() {
+ super(SliceService.class);
+ }
+
+ @Override
+ public SliceService deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
+ JsonNode node = jp.getCodec().readTree(jp);
+ return parseSliceService(node);
+ }
+
+ private SliceService parseSliceService(JsonNode node) {
+ SliceService service = new SliceService();
+
+ if (node.has("id")) service.setId(node.get("id").asText());
+ if (node.has("description")) service.setDescription(node.get("description").asText());
+ if (node.has("test-only")) service.setTestOnly(node.get("test-only").asBoolean(false));
+ if (node.has("testOnly")) service.setTestOnly(node.get("testOnly").asBoolean(false));
+
+ // SLO/SLE template: "slo-sle-template": "silver" (direct string id)
+ if (node.has("slo-sle-template") && node.get("slo-sle-template").isTextual()) {
+ SloSleTemplate t = new SloSleTemplate();
+ t.setId(node.get("slo-sle-template").asText());
+ service.setSloSleTemplate(t);
+ }
+ // SLO/SLE template nested under slo-sle-policy container
+ if (node.has("slo-sle-policy")) {
+ JsonNode policy = node.get("slo-sle-policy");
+ if (policy.has("slo-sle-template")) {
+ SloSleTemplate t = new SloSleTemplate();
+ t.setId(policy.get("slo-sle-template").asText());
+ service.setSloSleTemplate(t);
+ }
+ }
+
+ // Service tags
+ if (node.has("service-tags")) {
+ service.getServiceTags().addAll(parseServiceTags(node.get("service-tags")));
+ }
+
+ // Status
+ if (node.has("status")) {
+ service.setStatus(parseStatus(node.get("status")));
+ }
+
+ // SDPs — RFC 9543: { "sdp": [...] }
+ if (node.has("sdps")) {
+ JsonNode sdpsNode = node.get("sdps");
+ if (sdpsNode.has("sdp") && sdpsNode.get("sdp").isArray()) {
+ for (JsonNode sdpNode : sdpsNode.get("sdp")) {
+ service.getSdps().add(parseSdp(sdpNode));
+ }
+ } else if (sdpsNode.isArray()) {
+ for (JsonNode sdpNode : sdpsNode) {
+ service.getSdps().add(parseSdp(sdpNode));
+ }
+ }
+ }
+
+ // Connection groups — RFC 9543: { "connection-group": [...] }
+ if (node.has("connection-groups")) {
+ JsonNode cgRoot = node.get("connection-groups");
+ if (cgRoot.has("connection-group") && cgRoot.get("connection-group").isArray()) {
+ for (JsonNode cgNode : cgRoot.get("connection-group")) {
+ service.getConnectionGroups().add(parseConnectionGroup(cgNode));
+ }
+ } else if (cgRoot.isArray()) {
+ for (JsonNode cgNode : cgRoot) {
+ service.getConnectionGroups().add(parseConnectionGroup(cgNode));
+ }
+ }
+ }
+
+ logger.debug("Parsed RFC 9543 SliceService: {}", service.getId());
+ return service;
+ }
+
+ /**
+ * Parses service-tags from RFC 9543 format:
+ * { "tag-type": [ { "tag-type": "service", "tag-type-value": ["L3"] } ] }
+ */
+ private List parseServiceTags(JsonNode tagsNode) {
+ List tags = new ArrayList<>();
+ if (!tagsNode.has("tag-type") || !tagsNode.get("tag-type").isArray()) return tags;
+
+ for (JsonNode tagTypeNode : tagsNode.get("tag-type")) {
+ String typeStr = tagTypeNode.has("tag-type") ? tagTypeNode.get("tag-type").asText() : "";
+ ServiceTagType tagType = null;
+ try { tagType = ServiceTagType.valueOf(typeStr); } catch (Exception ignored) {}
+
+ if (tagTypeNode.has("tag-type-value") && tagTypeNode.get("tag-type-value").isArray()) {
+ for (JsonNode valueNode : tagTypeNode.get("tag-type-value")) {
+ ServiceTag tag = new ServiceTag();
+ tag.setType(tagType);
+ tag.setValue(valueNode.asText());
+ tags.add(tag);
+ }
+ }
+ }
+ return tags;
+ }
+
+ private ServiceStatus parseStatus(JsonNode statusNode) {
+ ServiceStatus status = new ServiceStatus();
+ if (statusNode.has("admin-status"))
+ status.setAdminStatus( statusNode.get("admin-status").asText() );
+ if ( statusNode.has("oper-status") )
+ status.setOperStatus( statusNode.get("oper-status").asText() );
+ return status;
+ }
+
+ private SDP parseSdp(JsonNode sdpNode) {
+ SDP sdp = new SDP();
+ if (sdpNode.has("id")) sdp.setId(sdpNode.get("id").asText());
+ if (sdpNode.has("node-id")) sdp.setNodeId(sdpNode.get("node-id").asText());
+ if (sdpNode.has("tp-ref")) sdp.setTpRef(sdpNode.get("tp-ref").asText());
+ if (sdpNode.has("geo-location") && !sdpNode.get("geo-location").isObject())
+ sdp.setGeoLocation(sdpNode.get("geo-location").asText());
+ if (sdpNode.has("sdp-ip-address")) {
+ JsonNode ipNode = sdpNode.get("sdp-ip-address");
+ if (ipNode.isArray()) {
+ for (JsonNode ip : ipNode) sdp.getSdpIpAddress().add(ip.asText());
+ } else if (!ipNode.isObject()) {
+ sdp.getSdpIpAddress().add(ipNode.asText());
+ }
+ }
+ return sdp;
+ }
+
+ private ConnectionGroup parseConnectionGroup(JsonNode node) {
+ ConnectionGroup cg = new ConnectionGroup();
+ if (node.has("id")) cg.setId(node.get("id").asText());
+ if (node.has("connectivity-type")) cg.setConnectivityType(
+ ConnectivityType.fromValue(node.get("connectivity-type").asText()));
+
+ // connectivity-construct list
+ if (node.has("connectivity-construct") && node.get("connectivity-construct").isArray()) {
+ for (JsonNode ccNode : node.get("connectivity-construct")) {
+ ConnectivityConstruct cc = new ConnectivityConstruct();
+ if (ccNode.has("id")) cc.setId(ccNode.get("id").asText());
+ if (ccNode.has("p2p-sender-sdp")) cc.setP2pSenderSdp(ccNode.get("p2p-sender-sdp").asText());
+ if (ccNode.has("p2p-receiver-sdp")) cc.setP2pReceiverSdp(ccNode.get("p2p-receiver-sdp").asText());
+ cg.getConnectivityConstructs().add(cc);
+ }
+ }
+ return cg;
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/Rfc9543SliceServiceSerializer.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/Rfc9543SliceServiceSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..1cab11db29bf6cb1f3f89a2b57811f630d819c3c
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/Rfc9543SliceServiceSerializer.java
@@ -0,0 +1,134 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import java.io.IOException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Custom Jackson serializer for RFC 9543 formatted SliceService JSON.
+ *
+ * Produces the standard RFC 9543 wire format with proper nested containers:
+ * - "sdps": { "sdp": [...] }
+ * - "connection-groups": { "connection-group": [...] }
+ * - "service-tags": { "tag-type": [ { "tag-type": "...", "tag-type-value": [...] } ] }
+ * - "slo-sle-template": "" (string reference, not object)
+ */
+public class Rfc9543SliceServiceSerializer extends StdSerializer {
+
+ private static final Logger logger = LoggerFactory.getLogger(Rfc9543SliceServiceSerializer.class);
+
+ public Rfc9543SliceServiceSerializer() {
+ super(SliceService.class);
+ }
+
+ @Override
+ public void serialize(SliceService s, JsonGenerator gen, SerializerProvider provider)
+ throws IOException {
+ gen.writeStartObject();
+
+ writeIfNotNull(gen, "id", s.getId());
+ writeIfNotNull(gen, "description", s.getDescription());
+
+ if (Boolean.TRUE.equals(s.getTestOnly())) {
+ gen.writeBooleanField("test-only", true);
+ }
+
+ // slo-sle-template as a plain string id reference
+ if (s.getSloSleTemplate() != null && s.getSloSleTemplate().getId() != null) {
+ gen.writeStringField("slo-sle-template", s.getSloSleTemplate().getId());
+ }
+
+ // service-tags: { "tag-type": [ { "tag-type": "...", "tag-type-value": [...] } ] }
+ if (s.getServiceTags() != null && !s.getServiceTags().isEmpty()) {
+ gen.writeFieldName("service-tags");
+ gen.writeStartObject();
+ gen.writeFieldName("tag-type");
+ gen.writeStartArray();
+ for (ServiceTag tag : s.getServiceTags()) {
+ gen.writeStartObject();
+ if (tag.getType() != null) gen.writeStringField("tag-type", tag.getType().name());
+ gen.writeFieldName("tag-type-value");
+ gen.writeStartArray();
+ if (tag.getValue() != null) gen.writeString(tag.getValue());
+ gen.writeEndArray();
+ gen.writeEndObject();
+ }
+ gen.writeEndArray();
+ gen.writeEndObject();
+ }
+
+ // status: { "admin-status": "...", "oper-status": "..." }
+ if (s.getStatus() != null) {
+ ServiceStatus st = s.getStatus();
+ gen.writeFieldName("status");
+ gen.writeStartObject();
+ writeIfNotNull(gen, "admin-status", st.getAdminStatus());
+ writeIfNotNull(gen, "oper-status", st.getOperStatus());
+ gen.writeEndObject();
+ }
+
+ // sdps: { "sdp": [ { "id": "...", "node-id": "...", ... } ] }
+ if (s.getSdps() != null && !s.getSdps().isEmpty()) {
+ gen.writeFieldName("sdps");
+ gen.writeStartObject();
+ gen.writeFieldName("sdp");
+ gen.writeStartArray();
+ for (SDP sdp : s.getSdps()) {
+ gen.writeStartObject();
+ writeIfNotNull(gen, "id", sdp.getId());
+ writeIfNotNull(gen, "node-id", sdp.getNodeId());
+ writeIfNotNull(gen, "tp-ref", sdp.getTpRef());
+ writeIfNotNull(gen, "geo-location", sdp.getGeoLocation());
+ if (sdp.getSdpIpAddress() != null && !sdp.getSdpIpAddress().isEmpty()) {
+ gen.writeFieldName("sdp-ip-address");
+ gen.writeStartArray();
+ for (String ip : sdp.getSdpIpAddress()) gen.writeString(ip);
+ gen.writeEndArray();
+ }
+ gen.writeEndObject();
+ }
+ gen.writeEndArray();
+ gen.writeEndObject();
+ }
+
+ // connection-groups: { "connection-group": [ { "id": "...", "connectivity-type": "...", "connectivity-construct": [...] } ] }
+ if (s.getConnectionGroups() != null && !s.getConnectionGroups().isEmpty()) {
+ gen.writeFieldName("connection-groups");
+ gen.writeStartObject();
+ gen.writeFieldName("connection-group");
+ gen.writeStartArray();
+ for (ConnectionGroup cg : s.getConnectionGroups()) {
+ gen.writeStartObject();
+ writeIfNotNull(gen, "id", cg.getId());
+ if (cg.getConnectivityType() != null) {
+ gen.writeStringField("connectivity-type", cg.getConnectivityType().toValue());
+ }
+ if (cg.getConnectivityConstructs() != null && !cg.getConnectivityConstructs().isEmpty()) {
+ gen.writeFieldName("connectivity-construct");
+ gen.writeStartArray();
+ for (ConnectivityConstruct cc : cg.getConnectivityConstructs()) {
+ gen.writeStartObject();
+ writeIfNotNull(gen, "id", cc.getId());
+ writeIfNotNull(gen, "p2p-sender-sdp", cc.getP2pSenderSdp());
+ writeIfNotNull(gen, "p2p-receiver-sdp", cc.getP2pReceiverSdp());
+ gen.writeEndObject();
+ }
+ gen.writeEndArray();
+ }
+ gen.writeEndObject();
+ }
+ gen.writeEndArray();
+ gen.writeEndObject();
+ }
+
+ gen.writeEndObject();
+ logger.debug("Serialized RFC 9543 SliceService: {}", s.getId());
+ }
+
+ private void writeIfNotNull(JsonGenerator gen, String field, String value) throws IOException {
+ if (value != null) gen.writeStringField(field, value);
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/Rfc9543SloSleTemplateDeserializer.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/Rfc9543SloSleTemplateDeserializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d101ae13ae8f60df6344c2e26c43bc7e5e2741c
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/Rfc9543SloSleTemplateDeserializer.java
@@ -0,0 +1,64 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import java.io.IOException;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.SlePolicy;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.SliceTemplateRef;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.SloPolicy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Custom Jackson deserializer for RFC 9543 formatted SloSleTemplate JSON.
+ *
+ * Maps RFC 9543 hyphenated top-level field names:
+ * - "slo-policy" → sloPolicy
+ * - "sle-policy" → slePolicy
+ * - "template-ref" → templateRef
+ *
+ * Nested SloPolicy and SlePolicy parsing is delegated back to Jackson,
+ * which honours the @JsonProperty annotations on those classes
+ * (e.g. "metric-bound" → metricBounds, "max-occupancy-level" → maxOccupancyLevel,
+ * "isolation-requirement" → isolation).
+ */
+public class Rfc9543SloSleTemplateDeserializer extends StdDeserializer {
+
+ private static final Logger logger = LoggerFactory.getLogger(Rfc9543SloSleTemplateDeserializer.class);
+
+ public Rfc9543SloSleTemplateDeserializer() {
+ super(SloSleTemplate.class);
+ }
+
+ @Override
+ public SloSleTemplate deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
+ JsonNode node = jp.getCodec().readTree(jp);
+ ObjectMapper codec = (ObjectMapper) jp.getCodec();
+ return parseTemplate(node, codec);
+ }
+
+ private SloSleTemplate parseTemplate(JsonNode node, ObjectMapper codec) throws IOException {
+ SloSleTemplate t = new SloSleTemplate();
+
+ if (node.has("id")) t.setId(node.get("id").asText());
+ if (node.has("description")) t.setDescription(node.get("description").asText());
+
+ if (node.has("slo-policy")) {
+ t.setSloPolicy(codec.treeToValue(node.get("slo-policy"), SloPolicy.class));
+ }
+
+ if (node.has("sle-policy")) {
+ t.setSlePolicy(codec.treeToValue(node.get("sle-policy"), SlePolicy.class));
+ }
+
+ if (node.has("template-ref")) {
+ t.setTemplateRef(codec.treeToValue(node.get("template-ref"), SliceTemplateRef.class));
+ }
+
+ logger.debug("Parsed RFC 9543 SloSleTemplate: {}", t.getId());
+ return t;
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/Rfc9543SloSleTemplateSerializer.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/Rfc9543SloSleTemplateSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..f37bc9645d45a8eaf25d7b4e23f5f2694acee32a
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/Rfc9543SloSleTemplateSerializer.java
@@ -0,0 +1,59 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import java.io.IOException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Custom Jackson serializer for RFC 9543 formatted SloSleTemplate JSON.
+ *
+ * Writes only the known RFC 9543 wire fields (id, description, slo-policy,
+ * sle-policy, template-ref), preventing interface default methods from
+ * LogicalResourceSpecMappable / RelatedManagedResourceReference from
+ * appearing as unwanted JSON properties.
+ *
+ * Nested SloPolicy and SlePolicy objects are delegated to Jackson's default
+ * serialization, which honours the @JsonProperty annotations on those classes.
+ */
+public class Rfc9543SloSleTemplateSerializer extends StdSerializer {
+
+ private static final Logger logger = LoggerFactory.getLogger(Rfc9543SloSleTemplateSerializer.class);
+
+ public Rfc9543SloSleTemplateSerializer() {
+ super(SloSleTemplate.class);
+ }
+
+ @Override
+ public void serialize(SloSleTemplate t, JsonGenerator gen, SerializerProvider provider)
+ throws IOException {
+ gen.writeStartObject();
+
+ writeIfNotNull(gen, "id", t.getId());
+ writeIfNotNull(gen, "description", t.getDescription());
+
+ if (t.getSloPolicy() != null) {
+ gen.writeFieldName("slo-policy");
+ provider.defaultSerializeValue(t.getSloPolicy(), gen);
+ }
+
+ if (t.getSlePolicy() != null) {
+ gen.writeFieldName("sle-policy");
+ provider.defaultSerializeValue(t.getSlePolicy(), gen);
+ }
+
+ if (t.getTemplateRef() != null) {
+ gen.writeFieldName("template-ref");
+ provider.defaultSerializeValue(t.getTemplateRef(), gen);
+ }
+
+ gen.writeEndObject();
+ logger.debug("Serialized RFC 9543 SloSleTemplate: {}", t.getId());
+ }
+
+ private void writeIfNotNull(JsonGenerator gen, String field, String value) throws IOException {
+ if (value != null) gen.writeStringField(field, value);
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/SDP.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/SDP.java
new file mode 100644
index 0000000000000000000000000000000000000000..ad5d1b1e73655aac8967cad93d8622fdb9c7d44d
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/SDP.java
@@ -0,0 +1,30 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Service Demarcation Point (SDP) - represents an edge point of a network slice service.
+ * SDPs are where customer traffic enters and exits the slice, and can be associated with
+ * RFC8345 topology termination points.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class SDP {
+ private String id;
+ private String description;
+ private String nodeId;
+ private String tpRef; // leafref into topology TP
+ private List sdpIpAddress = new ArrayList<>(); // ip-address list
+ private String geoLocation;
+
+ // 1-to-many relationship: SDP contains multiple match criteria
+ private List serviceMatchCriteria = new ArrayList<>();
+
+ // 0-to-1 relationship: optional attachment circuit
+ private AttachmentCircuit attachmentCircuit;
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/SdpMember.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/SdpMember.java
new file mode 100644
index 0000000000000000000000000000000000000000..64f9def49caf59d47e59677d108922436bb9ad97
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/SdpMember.java
@@ -0,0 +1,16 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents a reference to an SDP that is a member of a connectivity construct.
+ * Used for logical membership relationships (P2P, P2MP, A2A) in connectivity constructs.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class SdpMember {
+ private SDP sdpRef;
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ServiceStatus.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ServiceStatus.java
new file mode 100644
index 0000000000000000000000000000000000000000..f333796ba13b06c56e277d5194b9a24cab09c2a0
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ServiceStatus.java
@@ -0,0 +1,23 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import java.time.OffsetDateTime;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents the status of a network slice service.
+ * Includes administrative state, operational state, and last change timestamp.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ServiceStatus {
+ @JsonProperty("admin-status")
+ private String adminStatus;
+ @JsonProperty("oper-status")
+ private String operStatus;
+ @JsonProperty("last-change")
+ private OffsetDateTime lastChange;
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ServiceTag.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ServiceTag.java
new file mode 100644
index 0000000000000000000000000000000000000000..78dcae3a28f2be7a3396145b9dd81aeaac15be62
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ServiceTag.java
@@ -0,0 +1,17 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents a service tag for network slice services.
+ * Tags can be of different types (customer, service, opaque) and carry a value.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ServiceTag {
+ private ServiceTagType type;
+ private String value;
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ServiceTagType.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ServiceTagType.java
new file mode 100644
index 0000000000000000000000000000000000000000..0626f0b6e1e8b623ea3edd76a1f5da25b126d417
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/ServiceTagType.java
@@ -0,0 +1,10 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+/**
+ * Enumeration for service tag types as per IETF Network Slice Service specification.
+ */
+public enum ServiceTagType {
+ customer,
+ service,
+ opaque
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/SliceService.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/SliceService.java
new file mode 100644
index 0000000000000000000000000000000000000000..fe27113eb66d3627974fe7a88e544202ca5fdeb2
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/SliceService.java
@@ -0,0 +1,44 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents a customer-facing Network Slice Service (NSS).
+ * Includes service configuration, status, and all connectivity and resource parameters.
+ * Supports optional test-only feasibility mode and service-level SLO/SLE template reference.
+ *
+ * Uses custom RFC 9543 deserializer to handle hyphenated field names in JSON:
+ * - "service-tags" → serviceTags
+ * - "slo-sle-policy" → sloSleTemplate
+ * - "connection-groups" → connectionGroups
+ * - etc.
+ */
+@JsonDeserialize(using = Rfc9543SliceServiceDeserializer.class)
+@JsonSerialize(using = Rfc9543SliceServiceSerializer.class)
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class SliceService {
+ private String id;
+ private String description;
+ private Boolean testOnly = false;
+ private ServiceStatus status;
+
+ // 0-to-1 relationship: references an SLO/SLE template at the service level
+ private SloSleTemplate sloSleTemplate;
+
+ // 0..* relationship: service tags for classification and management
+ private List serviceTags = new ArrayList<>();
+
+ // 1-to-many relationship: contains multiple SDPs (service demarcation points)
+ private List sdps = new ArrayList<>();
+
+ // 1-to-many relationship: contains multiple connection groups
+ private List connectionGroups = new ArrayList<>();
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/SloSleTemplate.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/SloSleTemplate.java
new file mode 100644
index 0000000000000000000000000000000000000000..d240e79ff157098e2b908861c5cbbbe6c2c78de2
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/SloSleTemplate.java
@@ -0,0 +1,161 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model;
+
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.SlePolicy;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.SliceTemplateRef;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.SloPolicy;
+import org.etsi.osl.controllers.ietf.ns.domain.common.ExcludeFromMapping;
+import org.etsi.osl.controllers.ietf.ns.domain.common.LogicalResourceSpecMappable;
+import org.etsi.osl.controllers.ietf.ns.domain.common.RelatedManagedResourceReference;
+import org.etsi.osl.tmf.ri639.model.LogicalResource;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import jakarta.persistence.Transient;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Represents a reusable Service Level Objective (SLO) and Service Level Expectation (SLE) template.
+ *
+ * SLO/SLE templates encapsulate comprehensive service level definitions including:
+ * - SLO (Service Level Objectives): Performance metrics and targets
+ * - Availability percentage
+ * - Maximum Transmission Unit (MTU)
+ * - Performance metric bounds (latency, bandwidth, jitter, loss, etc.)
+ *
+ * - SLE (Service Level Expectations): Service assurance characteristics
+ * - Security requirements
+ * - Isolation requirements
+ * - Maximum occupancy level
+ * - Path constraints and diversity
+ *
+ * These templates can be referenced and reused by:
+ * - SliceService (service-level default policy)
+ * - ConnectionGroup (group-level policy)
+ * - ConnectivityConstruct (construct-level policy)
+ *
+ * Template composition is supported through SliceTemplateRef, allowing
+ * templates to reference other templates to build composite policies.
+ *
+ * Based on draft-ietf-teas-ietf-network-slice-nbi-yang-25
+ * /network-slice-services/slo-sle-templates/slo-sle-template
+ */
+@JsonDeserialize(using = Rfc9543SloSleTemplateDeserializer.class)
+@JsonSerialize(using = Rfc9543SloSleTemplateSerializer.class)
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Slf4j
+public class SloSleTemplate implements LogicalResourceSpecMappable, RelatedManagedResourceReference {
+
+ /**
+ * Unique identifier for the SLO/SLE template.
+ * Must be unique within the network-slice-services container.
+ */
+ private String id;
+
+ /**
+ * Human-readable description of the template.
+ * Explains the purpose and use case of the template.
+ * Optional (0..1 cardinality).
+ */
+ @ExcludeFromMapping(reason = "Mapped as LogicalResource.description")
+ private String description;
+
+ /**
+ * Service Level Objectives (SLO) policy.
+ * Contains performance targets and constraints:
+ * - Availability percentage
+ * - MTU size
+ * - Performance metric bounds
+ *
+ * Mandatory containment (1..1 cardinality).
+ */
+ @JsonProperty("slo-policy")
+ private SloPolicy sloPolicy;
+
+ /**
+ * Service Level Expectations (SLE) policy.
+ * Contains service assurance characteristics:
+ * - Security requirements
+ * - Isolation requirements
+ * - Maximum occupancy level
+ * - Path constraints
+ *
+ * Mandatory containment (1..1 cardinality).
+ */
+ @JsonProperty("sle-policy")
+ private SlePolicy slePolicy;
+
+
+ /**
+ * Reference to another SLO/SLE template for composition.
+ * Allows templates to build upon existing templates by reference.
+ * Optional (0..1 cardinality).
+ *
+ * Use case: Creating specialized templates by extending a base template.
+ * Example: A "premium-bandwidth" template might reference a "gold-template"
+ * and add additional bandwidth constraints.
+ */
+ private SliceTemplateRef templateRef;
+
+ /**
+ * ID of the related managed resource (e.g., a related device resource, managed component from another e.g. kubernetes controller)
+ * References another managed resource that this device is related to
+ * This field can be set by clients to establish relationships to other managed resources
+ */
+ @JsonIgnore
+ private String relatedManagedResourceId;
+
+ @ExcludeFromMapping(reason = "Service-managed relationship, not part of mapping")
+ @Transient
+ @JsonIgnore
+ private LogicalResource relatedManagedResource;
+
+
+
+
+ /**
+ * Creates a template with SLO and SLE policies.
+ * Convenience constructor for common template creation.
+ */
+ public SloSleTemplate(String id, String description, SloPolicy sloPolicy, SlePolicy slePolicy) {
+ this.id = id;
+ this.description = description;
+ this.sloPolicy = sloPolicy;
+ this.slePolicy = slePolicy;
+ }
+
+ /**
+ * Creates a template with reference to another template.
+ * Convenience constructor for composite template creation.
+ */
+ public SloSleTemplate(String id, SliceTemplateRef templateRef) {
+ this.id = id;
+ this.templateRef = templateRef;
+ }
+
+ @Override
+ @JsonIgnore
+ public String getEntityId() {
+ return this.id;
+ }
+
+ @Override
+ @JsonIgnore
+ public String getEntityName() {
+ return this.id;
+ }
+
+ @Override
+ @JsonIgnore
+ public String getEntityDescription() {
+ return this.description;
+ }
+
+
+
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/AvailabilityType.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/AvailabilityType.java
new file mode 100644
index 0000000000000000000000000000000000000000..ceb1e816e1d0c698469d1f202c2e70f2271a8a27
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/AvailabilityType.java
@@ -0,0 +1,39 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents availability constraints for a network slice service.
+ *
+ * Availability metrics express the percentage of time the service
+ * is expected to be operational and accessible. This includes both
+ * the service availability percentage and the commitment period.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class AvailabilityType {
+ /**
+ * Availability percentage (0.0 to 100.0)
+ * Examples: 99.0, 99.9, 99.99, 99.999
+ */
+ @JsonAlias({"availability-percentage"})
+ private Double availabilityPercentage;
+
+ /**
+ * Commitment period for the availability SLO
+ * Examples: "per-month", "per-year", "per-service-life"
+ */
+ @JsonAlias({"commitment-period"})
+ private String commitmentPeriod;
+
+ /**
+ * Downtime allowed per period in minutes
+ * Calculated as (100 - availabilityPercentage) * periodLength
+ */
+ @JsonAlias({"allowed-downtime-minutes"})
+ private Long allowedDowntimeMinutes;
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/Diversity.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/Diversity.java
new file mode 100644
index 0000000000000000000000000000000000000000..b0c7d06879e52822e04e59a53f378c6941fe10a9
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/Diversity.java
@@ -0,0 +1,35 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents path diversity constraints for network slices.
+ *
+ * Diversity constraints ensure that connectivity constructs within a
+ * network slice service follow diverse paths through the network, providing
+ * protection against single points of failure (SPOF).
+ *
+ * Based on draft-ietf-teas-ietf-network-slice-nbi-yang-25 and
+ * te-types:te-path-disjointness definitions.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class Diversity {
+
+ /**
+ * Type of path disjointness requirement.
+ * Defines the level of separation required between paths.
+ *
+ * Examples:
+ * - LINK: Paths must not share any link
+ * - NODE: Paths must not share any intermediate node
+ * - SRLG: Paths must not share any Shared Risk Link Group
+ * - LINK_AND_NODE: Paths must be both link-disjoint and node-disjoint
+ *
+ * Optional (0..1 cardinality).
+ */
+ private TePathDisjointness diversityType;
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/MetricBound.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/MetricBound.java
new file mode 100644
index 0000000000000000000000000000000000000000..9243f2aabe0549f4eedfc8f078ff8db289da904f
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/MetricBound.java
@@ -0,0 +1,86 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle;
+
+import java.math.BigDecimal;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents a metric bound for Service Level Objectives (SLOs).
+ *
+ * A metric bound defines upper limits (or thresholds) for a specific
+ * performance metric. Each bound includes:
+ * - The metric type (e.g., latency, bandwidth, loss)
+ * - The measurement unit (e.g., ms, Mbps, %)
+ * - The upper limit value
+ * - Optional percentile value for percentile-based metrics
+ *
+ * Based on draft-ietf-teas-ietf-network-slice-nbi-yang-25 MetricBound definition.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class MetricBound {
+
+ /**
+ * Type of SLO metric being bounded.
+ * Examples: ONE_WAY_DELAY_MAXIMUM, TWO_WAY_BANDWIDTH, ONE_WAY_PACKET_LOSS
+ */
+ @JsonProperty("metric-type")
+ private ServiceSloMetricType metricType;
+
+ /**
+ * Unit of measurement for the metric.
+ */
+ @JsonProperty("metric-unit")
+ private String metricUnit;
+
+ /**
+ * Optional human-readable description of the metric bound.
+ */
+ @JsonProperty("value-description")
+ private String valueDescription;
+
+ /**
+ * Percentile value for percentile-based metrics (0.0 to 100.0).
+ */
+ @JsonProperty("percentile-value")
+ private BigDecimal percentileValue;
+
+ /**
+ * Upper bound limit for the metric.
+ * Value is uint64 (0 to 18,446,744,073,709,551,615).
+ *
+ * Value interpretation depends on metric type and unit:
+ * - 0 indicates unbounded (no upper limit)
+ * - Positive value is the maximum allowed value
+ *
+ * Examples:
+ * - For latency: bound=50 with metricUnit="ms" means max 50 milliseconds
+ * - For bandwidth: bound=1000 with metricUnit="Mbps" means at least 1000 Mbps
+ * - For loss: bound=0.1 with metricUnit="%" means max 0.1% loss
+ */
+ private Long bound;
+
+ /**
+ * Creates a metric bound with type, unit, and bound value.
+ * Convenience constructor for simple metrics.
+ */
+ public MetricBound(ServiceSloMetricType metricType, String metricUnit, Long bound) {
+ this.metricType = metricType;
+ this.metricUnit = metricUnit;
+ this.bound = bound;
+ }
+
+ /**
+ * Creates a percentile-based metric bound.
+ * Convenience constructor for percentile metrics.
+ */
+ public MetricBound(ServiceSloMetricType metricType, String metricUnit, BigDecimal percentile, Long bound) {
+ this.metricType = metricType;
+ this.metricUnit = metricUnit;
+ this.percentileValue = percentile;
+ this.bound = bound;
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/PathConstraints.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/PathConstraints.java
new file mode 100644
index 0000000000000000000000000000000000000000..7d600a91c06ff8694f68265ae51876af488dde7e
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/PathConstraints.java
@@ -0,0 +1,40 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents path constraints for connectivity constructs within a network slice service.
+ *
+ * Path constraints define how connectivity constructs should be realized in the
+ * network, including:
+ * - Path diversity requirements (disjointness)
+ * - Service functions to apply along the path
+ * - Topology constraints
+ *
+ * Based on draft-ietf-teas-ietf-network-slice-nbi-yang-25 PathConstraints definition.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class PathConstraints {
+
+ /**
+ * Path diversity constraints ensuring that connectivity constructs
+ * follow diverse paths through the network.
+ * Optional containment (0..1 cardinality).
+ */
+ private Diversity diversity;
+
+ /**
+ * Service functions to apply along the connectivity construct paths.
+ * Optional containment (0..1 cardinality).
+ */
+ private ServiceFunctions serviceFunctions;
+
+ /**
+ * Additional path constraint parameters can be extended here
+ * as needed for specific implementations.
+ */
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/ServiceFunctions.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/ServiceFunctions.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2268948cb99198593312c7a94dd5570a127f6ca
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/ServiceFunctions.java
@@ -0,0 +1,56 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents service functions and capabilities for a network slice service.
+ *
+ * Service functions define additional services or capabilities that can be
+ * applied to the network slice, such as:
+ * - Deep packet inspection
+ * - Firewall functions
+ * - Load balancing
+ * - Traffic steering
+ * - NAT/PAT
+ * - QoS enforcement
+ *
+ * Based on draft-ietf-teas-ietf-network-slice-nbi-yang-25 ServiceFunctions definition.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ServiceFunctions {
+
+ /**
+ * List of service function identifiers or references.
+ * Each entry represents a specific service function capability
+ * that can be applied to the network slice.
+ *
+ * Examples:
+ * - "firewall"
+ * - "deep-packet-inspection"
+ * - "load-balancer"
+ * - "traffic-shaper"
+ * - "nat"
+ */
+ private List functions = new ArrayList<>();
+
+ /**
+ * Ordered flag indicating whether service functions must be applied
+ * in a specific order (service function chaining).
+ * If true, functions are applied in the order they appear in the list.
+ */
+ private Boolean ordered = false;
+
+ /**
+ * Adds a service function to the list.
+ * Convenience method.
+ */
+ public void addFunction(String function) {
+ this.functions.add(function);
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/ServiceIsolationType.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/ServiceIsolationType.java
new file mode 100644
index 0000000000000000000000000000000000000000..58def6a4a9ffe94340eae440d6875649c703cb46
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/ServiceIsolationType.java
@@ -0,0 +1,35 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Enumeration of Service Level Expectation (SLE) isolation types.
+ *
+ * Accepts both RFC 9543 kebab-case (e.g., "physical-isolation") and
+ * UPPER_SNAKE_CASE (e.g., "PHYSICAL_ISOLATION") during deserialization.
+ */
+public enum ServiceIsolationType {
+ PHYSICAL_ISOLATION,
+ LOGICAL_ISOLATION,
+ TRAFFIC_ISOLATION,
+ RESOURCE_ISOLATION,
+ DEDICATED_RESOURCES,
+ SHARED_RESOURCES_LIMITED;
+
+ @JsonCreator
+ public static ServiceIsolationType fromValue(String value) {
+ if (value == null || value.isBlank()) return null;
+ String normalized = value.toUpperCase().replace('-', '_');
+ try {
+ return ServiceIsolationType.valueOf(normalized);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ @JsonValue
+ public String toValue() {
+ return name().toLowerCase().replace('_', '-');
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/ServiceSecurityType.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/ServiceSecurityType.java
new file mode 100644
index 0000000000000000000000000000000000000000..387eb4d3ddf7aed9f5ea0901002822aa22f886f5
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/ServiceSecurityType.java
@@ -0,0 +1,35 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Enumeration of Service Level Expectation (SLE) security types.
+ *
+ * Accepts both RFC 9543 kebab-case (e.g., "encryption-required") and
+ * UPPER_SNAKE_CASE (e.g., "ENCRYPTION_REQUIRED") during deserialization.
+ */
+public enum ServiceSecurityType {
+ ENCRYPTION_REQUIRED,
+ AUTHENTICATION_REQUIRED,
+ INTEGRITY_PROTECTION,
+ CONFIDENTIALITY_REQUIRED,
+ SECURE_ROUTING,
+ VPN_REQUIRED;
+
+ @JsonCreator
+ public static ServiceSecurityType fromValue(String value) {
+ if (value == null || value.isBlank()) return null;
+ String normalized = value.toUpperCase().replace('-', '_');
+ try {
+ return ServiceSecurityType.valueOf(normalized);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ @JsonValue
+ public String toValue() {
+ return name().toLowerCase().replace('_', '-');
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/ServiceSloMetricType.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/ServiceSloMetricType.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b2d3af6e7c996a59b5915bf7f86a9bd6b5b1e47
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/ServiceSloMetricType.java
@@ -0,0 +1,60 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Enumeration of Service Level Objective (SLO) metric types.
+ *
+ * These metric types correspond to YANG identities in the
+ * draft-ietf-teas-ietf-network-slice-nbi-yang-25 specification and
+ * define the various performance metrics that can be monitored and
+ * constrained in network slice services.
+ *
+ * Accepts both RFC 9543 kebab-case (e.g., "two-way-bandwidth") and
+ * UPPER_SNAKE_CASE (e.g., "TWO_WAY_BANDWIDTH") during deserialization.
+ */
+public enum ServiceSloMetricType {
+ // Bandwidth metrics
+ ONE_WAY_BANDWIDTH,
+ TWO_WAY_BANDWIDTH,
+ SHARED_BANDWIDTH,
+
+ // Delay (absolute) metrics
+ ONE_WAY_DELAY_MAXIMUM,
+ TWO_WAY_DELAY_MAXIMUM,
+
+ // Delay (percentile) metrics
+ ONE_WAY_DELAY_PERCENTILE,
+ TWO_WAY_DELAY_PERCENTILE,
+
+ // Delay variation (absolute) metrics
+ ONE_WAY_DELAY_VARIATION_MAXIMUM,
+ TWO_WAY_DELAY_VARIATION_MAXIMUM,
+
+ // Delay variation (percentile) metrics
+ ONE_WAY_DELAY_VARIATION_PERCENTILE,
+ TWO_WAY_DELAY_VARIATION_PERCENTILE,
+
+ // Packet loss metrics
+ ONE_WAY_PACKET_LOSS,
+ TWO_WAY_PACKET_LOSS;
+
+ /** Deserialize from kebab-case RFC 9543 names or standard UPPER_SNAKE_CASE. */
+ @JsonCreator
+ public static ServiceSloMetricType fromValue(String value) {
+ if (value == null || value.isBlank()) return null;
+ String normalized = value.toUpperCase().replace('-', '_');
+ try {
+ return ServiceSloMetricType.valueOf(normalized);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ /** Serialize as lowercase kebab-case to stay RFC 9543 compliant. */
+ @JsonValue
+ public String toValue() {
+ return name().toLowerCase().replace('_', '-');
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/SlePolicy.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/SlePolicy.java
new file mode 100644
index 0000000000000000000000000000000000000000..c7a32e1614e797466559d880e78cccd1b59b302a
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/SlePolicy.java
@@ -0,0 +1,104 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle;
+
+import java.util.ArrayList;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents Service Level Expectations (SLE) policies for a network slice service.
+ *
+ * SLEs define the service assurance and operational characteristics that
+ * the network slice should maintain. Each SLE consists of:
+ * - Security requirements
+ * - Isolation requirements
+ * - Maximum occupancy level
+ * - Path constraints (diversity, service functions)
+ *
+ * Based on draft-ietf-teas-ietf-network-slice-nbi-yang-25 SlePolicy definition.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class SlePolicy {
+
+ /**
+ * List of security requirements for the network slice.
+ * Contains one or more ServiceSecurityType entries.
+ *
+ * Examples:
+ * - ENCRYPTION_REQUIRED
+ * - AUTHENTICATION_REQUIRED
+ * - INTEGRITY_PROTECTION
+ * - VPN_REQUIRED
+ *
+ * Optional leaf-list (can be empty).
+ */
+ private List security = new ArrayList<>();
+
+ /**
+ * List of isolation requirements for the network slice.
+ * Contains one or more ServiceIsolationType entries.
+ * Ensures the slice does not interfere with other slices.
+ *
+ * Examples:
+ * - PHYSICAL_ISOLATION
+ * - LOGICAL_ISOLATION
+ * - TRAFFIC_ISOLATION
+ * - RESOURCE_ISOLATION
+ *
+ * Optional leaf-list (can be empty).
+ */
+ @JsonProperty("isolation-requirement")
+ private List isolation = new ArrayList<>();
+
+ /**
+ * Maximum occupancy level as a percentage (1-100).
+ * Indicates the maximum percentage of network resources
+ * that this slice can consume. Helps prevent resource exhaustion.
+ *
+ * Range: 1 to 100
+ * Optional (0..1 cardinality).
+ *
+ * Examples:
+ * - 25 = max 25% of network resources
+ * - 50 = max 50% of network resources
+ * - 100 = can use all available resources
+ */
+ @JsonProperty("max-occupancy-level")
+ private Short maxOccupancyLevel;
+
+ /**
+ * Path constraints including diversity and service function requirements.
+ * Optional containment (0..1 cardinality).
+ */
+ @JsonProperty("path-constraints")
+ private PathConstraints pathConstraints;
+
+ /**
+ * Adds a security requirement to the SLE policy.
+ * Convenience method.
+ */
+ public void addSecurityRequirement(ServiceSecurityType securityType) {
+ this.security.add(securityType);
+ }
+
+ /**
+ * Adds an isolation requirement to the SLE policy.
+ * Convenience method.
+ */
+ public void addIsolationRequirement(ServiceIsolationType isolationType) {
+ this.isolation.add(isolationType);
+ }
+
+ /**
+ * Creates an SLE policy with security and isolation requirements.
+ * Convenience constructor.
+ */
+ public SlePolicy(List security, List isolation) {
+ this.security = security;
+ this.isolation = isolation;
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/SliceTemplateRef.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/SliceTemplateRef.java
new file mode 100644
index 0000000000000000000000000000000000000000..54ce1a09bfa68d320b8cddea148933960e6fbe3e
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/SliceTemplateRef.java
@@ -0,0 +1,29 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents a reference to another SLO/SLE template.
+ *
+ * This typedef allows templates to reference other templates, enabling
+ * template composition and reuse. A SliceTemplateRef contains a leafref
+ * to another SloSleTemplate by its ID.
+ *
+ * Based on draft-ietf-teas-ietf-network-slice-nbi-yang-25 SliceTemplateRef definition.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class SliceTemplateRef {
+
+ /**
+ * Reference to another SLO/SLE template ID.
+ * This is a leafref pointing to /network-slice-services/
+ * slo-sle-templates/slo-sle-template/id
+ *
+ * Allows creating composite templates that build upon existing templates.
+ */
+ private String ref;
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/SloPolicy.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/SloPolicy.java
new file mode 100644
index 0000000000000000000000000000000000000000..09c5cd7cfcf8ac3bbfbaa3dcf08a0455ba5267f1
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/SloPolicy.java
@@ -0,0 +1,85 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle;
+
+import java.util.ArrayList;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents Service Level Objectives (SLO) policies for a network slice service.
+ *
+ * SLOs define the target performance metrics that a network slice service
+ * should achieve. Each SLO consists of:
+ * - Availability constraints (uptime percentage)
+ * - Maximum Transmission Unit (MTU) size
+ * - One or more metric bounds (latency, bandwidth, loss, etc.)
+ *
+ * Based on draft-ietf-teas-ietf-network-slice-nbi-yang-25 SloPolicy definition.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class SloPolicy {
+
+ /**
+ * Availability constraint for the service.
+ * Specifies the expected uptime percentage and commitment period.
+ * Optional (0..1 cardinality).
+ */
+ private AvailabilityType availability;
+
+ /**
+ * Maximum Transmission Unit (MTU) size in bytes.
+ * Specifies the largest packet size that can be transmitted
+ * through the network slice service.
+ *
+ * Examples:
+ * - 1500 (Ethernet standard)
+ * - 1518 (Ethernet with VLAN)
+ * - 9000 (Jumbo frames)
+ * - 65535 (Maximum IP packet size)
+ *
+ * Optional (0..1 cardinality).
+ */
+ private Long mtu;
+
+ /**
+ * List of metric bounds defining SLO constraints.
+ * Contains one or more MetricBound entries, each defining
+ * a performance metric limit (latency, bandwidth, loss, etc.).
+ *
+ * Examples of metric bounds:
+ * - Maximum latency: 50 ms
+ * - Minimum bandwidth: 1000 Mbps
+ * - Maximum packet loss: 0.001%
+ */
+ @JsonProperty("metric-bound")
+ private List metricBounds = new ArrayList<>();
+
+ /**
+ * Adds a metric bound to the SLO policy.
+ * Convenience method for building SLO policies programmatically.
+ */
+ public void addMetricBound(MetricBound bound) {
+ this.metricBounds.add(bound);
+ }
+
+ /**
+ * Creates a basic SLO policy with availability only.
+ * Convenience constructor.
+ */
+ public SloPolicy(AvailabilityType availability) {
+ this.availability = availability;
+ }
+
+ /**
+ * Creates an SLO policy with availability and MTU.
+ * Convenience constructor.
+ */
+ public SloPolicy(AvailabilityType availability, Long mtu) {
+ this.availability = availability;
+ this.mtu = mtu;
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/TePathDisjointness.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/TePathDisjointness.java
new file mode 100644
index 0000000000000000000000000000000000000000..7c5a71036413de0a74bd1747c7fa4068fcfe3762
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/domain/model/slo_sle/TePathDisjointness.java
@@ -0,0 +1,26 @@
+package org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle;
+
+/**
+ * Enumeration of Traffic Engineering (TE) path disjointness constraints.
+ *
+ * These types correspond to YANG identities and te-types:te-path-disjointness
+ * in the draft-ietf-teas-ietf-network-slice-nbi-yang-25 specification.
+ *
+ * Path disjointness requirements ensure that connectivity constructs
+ * within a network slice service follow diverse paths through the network,
+ * providing protection against single points of failure.
+ *
+ * Disjointness levels:
+ * - LINK: Connectivity constructs must not share any link
+ * - NODE: Connectivity constructs must not share any intermediate node
+ * - SRLG: Connectivity constructs must not share any Shared Risk Link Group
+ */
+public enum TePathDisjointness {
+ LINK,
+ NODE,
+ SRLG,
+ LINK_AND_NODE,
+ LINK_AND_SRLG,
+ NODE_AND_SRLG,
+ LINK_NODE_AND_SRLG
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/restconf/RestconfClient.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/restconf/RestconfClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..21c23a0c5067a30541c47f4a454d223cd066de6e
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/restconf/RestconfClient.java
@@ -0,0 +1,281 @@
+package org.etsi.osl.controllers.ietf.ns.api.restconf;
+
+import java.util.List;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.NetworkSliceServices;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.SDP;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.SliceService;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.SloSleTemplate;
+
+/**
+ * RESTCONF Client interface for communicating with a Network Slice Service Provider.
+ *
+ * The IETF NS Controller acts as a Consumer (Network Slice Service Customer) that communicates
+ * with a Provider (Network Slice Controller) via RESTCONF (RFC 8040) to manage IETF
+ * Network Slice Services as defined in RFC 9543 and draft-ietf-teas-ietf-network-slice-nbi-yang-25.
+ *
+ * Operations are grouped by resource:
+ * 1. Container – /restconf/data/ietf-network-slice-service:network-slice-services
+ * 2. SliceService – .../slice-service and .../slice-service={id}
+ * 3. SDP – .../slice-service={id}/sdps and .../sdps/sdp={sdpId}
+ * 4. SLO/SLE Template – .../slo-sle-templates/slo-sle-template and ...={id}
+ */
+public interface RestconfClient {
+
+ // =========================================================================
+ // 1. Container – /restconf/data/ietf-network-slice-service:network-slice-services
+ // =========================================================================
+
+ /**
+ * Create the network-slice-services container with all nested services and templates.
+ *
+ * HTTP: POST /restconf/data/ietf-network-slice-service:network-slice-services
+ *
+ * @param networkSliceServices container with slice services and SLO/SLE templates
+ * @return the created container
+ * @throws RestconfException if the request fails
+ */
+ NetworkSliceServices createNetworkSliceServices(NetworkSliceServices networkSliceServices)
+ throws RestconfException;
+
+ /**
+ * Replace the entire network-slice-services container.
+ *
+ * HTTP: PUT /restconf/data/ietf-network-slice-service:network-slice-services
+ *
+ * @param networkSliceServices updated container
+ * @return the updated container
+ * @throws RestconfException if the request fails or nothing exists to update
+ */
+ NetworkSliceServices updateNetworkSliceServices(NetworkSliceServices networkSliceServices)
+ throws RestconfException;
+
+ /**
+ * Delete the entire network-slice-services container (all slices and templates).
+ *
+ * HTTP: DELETE /restconf/data/ietf-network-slice-service:network-slice-services
+ *
+ * @throws RestconfException if the request fails
+ */
+ void deleteAllNetworkSliceServices() throws RestconfException;
+
+ /**
+ * Retrieve the entire network-slice-services container.
+ *
+ * HTTP: GET /restconf/data/ietf-network-slice-service:network-slice-services
+ *
+ * @return the full container with all slice services and templates
+ * @throws RestconfException if the request fails or nothing is found
+ */
+ NetworkSliceServices getAllNetworkSliceServices() throws RestconfException;
+
+ // =========================================================================
+ // 2. SliceService – .../slice-service and .../slice-service={id}
+ // =========================================================================
+
+ /**
+ * Create a new network slice service.
+ *
+ * HTTP: POST /restconf/data/ietf-network-slice-service:network-slice-services/slice-service
+ *
+ * @param service the slice service to create
+ * @return the created slice service
+ * @throws RestconfException if the request fails or the service already exists (409)
+ */
+ SliceService createSliceService(SliceService service) throws RestconfException;
+
+ /**
+ * List all provisioned network slice services.
+ *
+ * HTTP: GET /restconf/data/ietf-network-slice-service:network-slice-services/slice-service
+ *
+ * @return list of all slice services
+ * @throws RestconfException if the request fails or none are found
+ */
+ List listSliceServices() throws RestconfException;
+
+ /**
+ * Delete all network slice services.
+ *
+ * HTTP: DELETE /restconf/data/ietf-network-slice-service:network-slice-services/slice-service
+ *
+ * @throws RestconfException if the request fails
+ */
+ void deleteAllSliceServices() throws RestconfException;
+
+ /**
+ * Retrieve a specific network slice service.
+ *
+ * HTTP: GET /restconf/data/ietf-network-slice-service:network-slice-services/slice-service={id}
+ *
+ * @param serviceId the unique identifier of the slice service
+ * @return the slice service with current configuration and status
+ * @throws RestconfException if the request fails or the service is not found
+ */
+ SliceService getSliceService(String serviceId) throws RestconfException;
+
+ /**
+ * Update an existing network slice service.
+ *
+ * HTTP: PUT /restconf/data/ietf-network-slice-service:network-slice-services/slice-service={id}
+ *
+ * @param serviceId the unique identifier of the slice service to update
+ * @param service the updated slice service configuration
+ * @return the updated slice service
+ * @throws RestconfException if the request fails or the service is not found
+ */
+ SliceService updateSliceService(String serviceId, SliceService service) throws RestconfException;
+
+ /**
+ * Delete a specific network slice service.
+ *
+ * HTTP: DELETE /restconf/data/ietf-network-slice-service:network-slice-services/slice-service={id}
+ *
+ * @param serviceId the unique identifier of the slice service to delete
+ * @throws RestconfException if the request fails or the service is not found
+ */
+ void deleteSliceService(String serviceId) throws RestconfException;
+
+ // =========================================================================
+ // 3. SDP – .../slice-service={id}/sdps and .../sdps/sdp={sdpId}
+ // =========================================================================
+
+ /**
+ * Add a new SDP to an existing slice service.
+ *
+ * HTTP: POST /restconf/data/.../slice-service={serviceId}/sdps
+ *
+ * @param serviceId the slice service that will own the SDP
+ * @param sdp the SDP to create
+ * @return the created SDP
+ * @throws RestconfException if the request fails or the SDP already exists (409)
+ */
+ SDP createSdp(String serviceId, SDP sdp) throws RestconfException;
+
+ /**
+ * List all SDPs of a slice service.
+ *
+ * HTTP: GET /restconf/data/.../slice-service={serviceId}/sdps
+ *
+ * @param serviceId the slice service whose SDPs to retrieve
+ * @return list of SDPs
+ * @throws RestconfException if the request fails
+ */
+ List listSdps(String serviceId) throws RestconfException;
+
+ /**
+ * Delete all SDPs of a slice service.
+ *
+ * HTTP: DELETE /restconf/data/.../slice-service={serviceId}/sdps
+ *
+ * @param serviceId the slice service whose SDPs to delete
+ * @throws RestconfException if the request fails
+ */
+ void deleteAllSdps(String serviceId) throws RestconfException;
+
+ /**
+ * Retrieve a specific SDP of a slice service.
+ *
+ * HTTP: GET /restconf/data/.../slice-service={serviceId}/sdps/sdp={sdpId}
+ *
+ * @param serviceId the slice service identifier
+ * @param sdpId the SDP identifier
+ * @return the SDP
+ * @throws RestconfException if the request fails or the SDP is not found
+ */
+ SDP getSdp(String serviceId, String sdpId) throws RestconfException;
+
+ /**
+ * Update a specific SDP of a slice service.
+ *
+ * HTTP: PUT /restconf/data/.../slice-service={serviceId}/sdps/sdp={sdpId}
+ *
+ * @param serviceId the slice service identifier
+ * @param sdpId the SDP identifier to update
+ * @param sdp the updated SDP configuration
+ * @return the updated SDP
+ * @throws RestconfException if the request fails or the SDP is not found
+ */
+ SDP updateSdp(String serviceId, String sdpId, SDP sdp) throws RestconfException;
+
+ /**
+ * Delete a specific SDP from a slice service.
+ *
+ * HTTP: DELETE /restconf/data/.../slice-service={serviceId}/sdps/sdp={sdpId}
+ *
+ * @param serviceId the slice service identifier
+ * @param sdpId the SDP identifier to delete
+ * @throws RestconfException if the request fails or the SDP is not found
+ */
+ void deleteSdp(String serviceId, String sdpId) throws RestconfException;
+
+ // =========================================================================
+ // 4. SLO/SLE Template – .../slo-sle-templates/slo-sle-template and ...={id}
+ // =========================================================================
+
+ /**
+ * Create a new SLO/SLE template.
+ *
+ * HTTP: POST /restconf/data/.../slo-sle-templates/slo-sle-template
+ *
+ * @param template the SLO/SLE template to create
+ * @return the created template
+ * @throws RestconfException if the request fails or the template already exists (409)
+ */
+ SloSleTemplate createSloSleTemplate(SloSleTemplate template) throws RestconfException;
+
+ /**
+ * List all SLO/SLE templates.
+ *
+ * HTTP: GET /restconf/data/.../slo-sle-templates/slo-sle-template
+ *
+ * Returns raw JSON so callers can use {@link org.etsi.osl.controllers.ietf.ns.api.restconf.Rfc9543JsonConverter}
+ * to parse the RFC 9543 namespace-wrapped response.
+ *
+ * @return JSON string containing RFC 9543 formatted SLO/SLE templates
+ * @throws RestconfException if the request fails
+ */
+ String getSloSleTemplates() throws RestconfException;
+
+ /**
+ * Delete all SLO/SLE templates.
+ *
+ * HTTP: DELETE /restconf/data/.../slo-sle-templates/slo-sle-template
+ *
+ * @throws RestconfException if the request fails
+ */
+ void deleteAllSloSleTemplates() throws RestconfException;
+
+ /**
+ * Retrieve a specific SLO/SLE template by ID.
+ *
+ * HTTP: GET /restconf/data/.../slo-sle-templates/slo-sle-template={id}
+ *
+ * @param templateId the unique identifier of the template
+ * @return the SLO/SLE template
+ * @throws RestconfException if the request fails or the template is not found
+ */
+ SloSleTemplate getSloSleTemplate(String templateId) throws RestconfException;
+
+ /**
+ * Update an existing SLO/SLE template.
+ *
+ * HTTP: PUT /restconf/data/.../slo-sle-templates/slo-sle-template={id}
+ *
+ * @param templateId the unique identifier of the template to update
+ * @param template the updated template configuration
+ * @return the updated template
+ * @throws RestconfException if the request fails or the template is not found
+ */
+ SloSleTemplate updateSloSleTemplate(String templateId, SloSleTemplate template)
+ throws RestconfException;
+
+ /**
+ * Delete a specific SLO/SLE template.
+ *
+ * HTTP: DELETE /restconf/data/.../slo-sle-templates/slo-sle-template={id}
+ *
+ * @param templateId the unique identifier of the template to delete
+ * @throws RestconfException if the request fails or the template is not found
+ */
+ void deleteSloSleTemplate(String templateId) throws RestconfException;
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/api/restconf/RestconfClientImpl.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/restconf/RestconfClientImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc2053b8865936c5c82c9ab12cc7193e68c6a4d7
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/api/restconf/RestconfClientImpl.java
@@ -0,0 +1,641 @@
+package org.etsi.osl.controllers.ietf.ns.api.restconf;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.List;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.NetworkSliceServices;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.SDP;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.SliceService;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.SloSleTemplate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.RestTemplate;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Implementation of {@link RestconfClient} that communicates with a Network Slice Service Provider
+ * over HTTP/HTTPS using Spring RestTemplate.
+ *
+ * All paths follow the swagger-documented RESTCONF resource tree rooted at:
+ * /restconf/data/ietf-network-slice-service:network-slice-services
+ *
+ * Configuration properties (application.yml):
+ * restconf.provider-url – base URL of the RESTCONF provider (default: http://localhost:11880)
+ * restconf.auth-method – authentication method: basic | none (default: basic)
+ * restconf.auth.username – HTTP Basic username (default: admin)
+ * restconf.auth.password – HTTP Basic password (default: admin123)
+ * restconf.timeout-ms – connection timeout in ms (default: 10000)
+ */
+@Component
+public class RestconfClientImpl implements RestconfClient {
+
+ private static final Logger logger = LoggerFactory.getLogger("org.etsi.osl.controllers.ietf.ns");
+
+ private static final String YANG_MODULE = "ietf-network-slice-service";
+ private static final String RESTCONF_BASE = "/restconf/data";
+ private static final String NSS_PATH = RESTCONF_BASE + "/" + YANG_MODULE + ":network-slice-services";
+
+ @Value("${restconf.provider-url:http://localhost:11880}")
+ private String providerUrl;
+
+ @Value("${restconf.auth-method:basic}")
+ private String authMethod;
+
+ @Value("${restconf.auth.username:admin}")
+ private String authUsername;
+
+ @Value("${restconf.auth.password:admin123}")
+ private String authPassword;
+
+ @Autowired
+ private RestTemplate restTemplate;
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ // =========================================================================
+ // 1. Container
+ // =========================================================================
+
+ @Override
+ public NetworkSliceServices createNetworkSliceServices(NetworkSliceServices networkSliceServices)
+ throws RestconfException {
+ String uri = nssUri();
+ logger.info("Creating network-slice-services container at {}", uri);
+ try {
+ String payloadJson = buildRfc9543Payload(networkSliceServices);
+ logger.debug("POST {} — request payload:\n{}", uri, payloadJson);
+
+ HttpEntity request = new HttpEntity<>(payloadJson, buildHeaders());
+ ResponseEntity resp = restTemplate.exchange(uri, HttpMethod.POST, request, String.class);
+ assertSuccess(resp, "create network-slice-services container");
+ return networkSliceServices;
+ } catch (HttpClientErrorException e) {
+ logger.error("POST {} — HTTP {} response body:\n{}", uri,
+ e.getStatusCode().value(), e.getResponseBodyAsString());
+ throw handleHttpError(e);
+ } catch (RestconfException e) {
+ throw e;
+ } catch (Exception e) {
+ throw wrap("create network-slice-services container", e);
+ }
+ }
+
+ @Override
+ public NetworkSliceServices updateNetworkSliceServices(NetworkSliceServices networkSliceServices)
+ throws RestconfException {
+ String uri = nssUri();
+ logger.info("Updating network-slice-services container");
+ try {
+ ResponseEntity resp = restTemplate.exchange(
+ uri, HttpMethod.PUT,
+ new HttpEntity<>(networkSliceServices, buildHeaders()),
+ NetworkSliceServices.class);
+ assertSuccess(resp, "update network-slice-services container");
+ return resp.getBody();
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (RestconfException e) {
+ throw e;
+ } catch (Exception e) {
+ throw wrap("update network-slice-services container", e);
+ }
+ }
+
+ @Override
+ public void deleteAllNetworkSliceServices() throws RestconfException {
+ String uri = nssUri();
+ logger.info("Deleting entire network-slice-services container");
+ try {
+ restTemplate.exchange(uri, HttpMethod.DELETE,
+ new HttpEntity<>(buildHeaders()), Void.class);
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (Exception e) {
+ throw wrap("delete network-slice-services container", e);
+ }
+ }
+
+ @Override
+ public NetworkSliceServices getAllNetworkSliceServices() throws RestconfException {
+ String uri = nssUri();
+ logger.debug("Retrieving entire network-slice-services container");
+ try {
+ ResponseEntity resp = restTemplate.exchange(
+ uri, HttpMethod.GET,
+ new HttpEntity<>(buildHeaders()),
+ NetworkSliceServices.class);
+ assertSuccess(resp, "get network-slice-services");
+ return resp.getBody();
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (RestconfException e) {
+ throw e;
+ } catch (Exception e) {
+ throw wrap("get network-slice-services", e);
+ }
+ }
+
+ // =========================================================================
+ // 2. SliceService
+ // =========================================================================
+
+ @Override
+ public SliceService createSliceService(SliceService service) throws RestconfException {
+ String uri = sliceServiceListUri();
+ logger.info("Creating network slice service: {}", service != null ? service.getId() : "null");
+ try {
+ ResponseEntity resp = restTemplate.exchange(
+ uri, HttpMethod.POST,
+ new HttpEntity<>(service, buildHeaders()),
+ SliceService.class);
+ assertSuccess(resp, "create slice service");
+ return resp.getBody();
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (RestconfException e) {
+ throw e;
+ } catch (Exception e) {
+ throw wrap("create slice service", e);
+ }
+ }
+
+ @Override
+ public List listSliceServices() throws RestconfException {
+ String uri = sliceServiceListUri();
+ logger.debug("Listing all network slice services");
+ try {
+ ResponseEntity resp = restTemplate.exchange(
+ uri, HttpMethod.GET,
+ new HttpEntity<>(buildHeaders()),
+ String.class);
+
+ if (resp.getStatusCode() == HttpStatus.NOT_FOUND) {
+ return new ArrayList<>();
+ }
+ assertSuccess(resp, "list slice services");
+
+ String body = resp.getBody();
+ if (body == null || body.isBlank()) {
+ return new ArrayList<>();
+ }
+ try {
+ return new Rfc9543JsonConverter().parseSliceServices(body);
+ } catch (Exception parseErr) {
+ logger.debug("RFC 9543 parse failed, falling back to direct array", parseErr);
+ SliceService[] arr = objectMapper.readValue(body, SliceService[].class);
+ return Arrays.asList(arr != null ? arr : new SliceService[0]);
+ }
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (RestconfException e) {
+ throw e;
+ } catch (Exception e) {
+ throw wrap("list slice services", e);
+ }
+ }
+
+ @Override
+ public void deleteAllSliceServices() throws RestconfException {
+ String uri = sliceServiceListUri();
+ logger.info("Deleting all network slice services");
+ try {
+ restTemplate.exchange(uri, HttpMethod.DELETE,
+ new HttpEntity<>(buildHeaders()), Void.class);
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (Exception e) {
+ throw wrap("delete all slice services", e);
+ }
+ }
+
+ @Override
+ public SliceService getSliceService(String serviceId) throws RestconfException {
+ String uri = sliceServiceUri(serviceId);
+ logger.debug("Retrieving network slice service: {}", serviceId);
+ try {
+ ResponseEntity resp = restTemplate.exchange(
+ uri, HttpMethod.GET,
+ new HttpEntity<>(buildHeaders()),
+ SliceService.class);
+ assertSuccess(resp, "get slice service " + serviceId);
+ return resp.getBody();
+ } catch (HttpClientErrorException.NotFound e) {
+ throw notFound("slice service", serviceId);
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (RestconfException e) {
+ throw e;
+ } catch (Exception e) {
+ throw wrap("get slice service " + serviceId, e);
+ }
+ }
+
+ @Override
+ public SliceService updateSliceService(String serviceId, SliceService service)
+ throws RestconfException {
+ String uri = sliceServiceUri(serviceId);
+ logger.info("Updating network slice service: {}", serviceId);
+ try {
+ ResponseEntity resp = restTemplate.exchange(
+ uri, HttpMethod.PUT,
+ new HttpEntity<>(service, buildHeaders()),
+ SliceService.class);
+ assertSuccess(resp, "update slice service " + serviceId);
+ return resp.getBody();
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (RestconfException e) {
+ throw e;
+ } catch (Exception e) {
+ throw wrap("update slice service " + serviceId, e);
+ }
+ }
+
+ @Override
+ public void deleteSliceService(String serviceId) throws RestconfException {
+ String uri = sliceServiceUri(serviceId);
+ logger.info("Deleting network slice service: {}", serviceId);
+ try {
+ restTemplate.exchange(uri, HttpMethod.DELETE,
+ new HttpEntity<>(buildHeaders()), Void.class);
+ } catch (HttpClientErrorException.NotFound e) {
+ throw notFound("slice service", serviceId);
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (Exception e) {
+ throw wrap("delete slice service " + serviceId, e);
+ }
+ }
+
+ // =========================================================================
+ // 3. SDP
+ // =========================================================================
+
+ @Override
+ public SDP createSdp(String serviceId, SDP sdp) throws RestconfException {
+ String uri = sdpListUri(serviceId);
+ logger.info("Creating SDP in slice service: {}", serviceId);
+ try {
+ ResponseEntity resp = restTemplate.exchange(
+ uri, HttpMethod.POST,
+ new HttpEntity<>(sdp, buildHeaders()),
+ SDP.class);
+ assertSuccess(resp, "create SDP in " + serviceId);
+ return resp.getBody();
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (RestconfException e) {
+ throw e;
+ } catch (Exception e) {
+ throw wrap("create SDP in " + serviceId, e);
+ }
+ }
+
+ @Override
+ public List listSdps(String serviceId) throws RestconfException {
+ String uri = sdpListUri(serviceId);
+ logger.debug("Listing SDPs for slice service: {}", serviceId);
+ try {
+ ResponseEntity resp = restTemplate.exchange(
+ uri, HttpMethod.GET,
+ new HttpEntity<>(buildHeaders()),
+ SDP[].class);
+ assertSuccess(resp, "list SDPs for " + serviceId);
+ SDP[] body = resp.getBody();
+ return Arrays.asList(body != null ? body : new SDP[0]);
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (RestconfException e) {
+ throw e;
+ } catch (Exception e) {
+ throw wrap("list SDPs for " + serviceId, e);
+ }
+ }
+
+ @Override
+ public void deleteAllSdps(String serviceId) throws RestconfException {
+ String uri = sdpListUri(serviceId);
+ logger.info("Deleting all SDPs for slice service: {}", serviceId);
+ try {
+ restTemplate.exchange(uri, HttpMethod.DELETE,
+ new HttpEntity<>(buildHeaders()), Void.class);
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (Exception e) {
+ throw wrap("delete all SDPs for " + serviceId, e);
+ }
+ }
+
+ @Override
+ public SDP getSdp(String serviceId, String sdpId) throws RestconfException {
+ String uri = sdpUri(serviceId, sdpId);
+ logger.debug("Retrieving SDP {} from slice service: {}", sdpId, serviceId);
+ try {
+ ResponseEntity resp = restTemplate.exchange(
+ uri, HttpMethod.GET,
+ new HttpEntity<>(buildHeaders()),
+ SDP.class);
+ assertSuccess(resp, "get SDP " + sdpId);
+ return resp.getBody();
+ } catch (HttpClientErrorException.NotFound e) {
+ throw notFound("SDP", sdpId);
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (RestconfException e) {
+ throw e;
+ } catch (Exception e) {
+ throw wrap("get SDP " + sdpId, e);
+ }
+ }
+
+ @Override
+ public SDP updateSdp(String serviceId, String sdpId, SDP sdp) throws RestconfException {
+ String uri = sdpUri(serviceId, sdpId);
+ logger.info("Updating SDP {} in slice service: {}", sdpId, serviceId);
+ try {
+ ResponseEntity resp = restTemplate.exchange(
+ uri, HttpMethod.PUT,
+ new HttpEntity<>(sdp, buildHeaders()),
+ SDP.class);
+ assertSuccess(resp, "update SDP " + sdpId);
+ return resp.getBody();
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (RestconfException e) {
+ throw e;
+ } catch (Exception e) {
+ throw wrap("update SDP " + sdpId, e);
+ }
+ }
+
+ @Override
+ public void deleteSdp(String serviceId, String sdpId) throws RestconfException {
+ String uri = sdpUri(serviceId, sdpId);
+ logger.info("Deleting SDP {} from slice service: {}", sdpId, serviceId);
+ try {
+ restTemplate.exchange(uri, HttpMethod.DELETE,
+ new HttpEntity<>(buildHeaders()), Void.class);
+ } catch (HttpClientErrorException.NotFound e) {
+ throw notFound("SDP", sdpId);
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (Exception e) {
+ throw wrap("delete SDP " + sdpId, e);
+ }
+ }
+
+ // =========================================================================
+ // 4. SLO/SLE Template
+ // =========================================================================
+
+ @Override
+ public SloSleTemplate createSloSleTemplate(SloSleTemplate template) throws RestconfException {
+ String uri = templateListUri();
+ logger.info("Creating SLO/SLE template: {}", template != null ? template.getId() : "null");
+ try {
+ ResponseEntity resp = restTemplate.exchange(
+ uri, HttpMethod.POST,
+ new HttpEntity<>(template, buildHeaders()),
+ SloSleTemplate.class);
+ assertSuccess(resp, "create SLO/SLE template");
+ return resp.getBody();
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (RestconfException e) {
+ throw e;
+ } catch (Exception e) {
+ throw wrap("create SLO/SLE template", e);
+ }
+ }
+
+ @Override
+ public String getSloSleTemplates() throws RestconfException {
+ String uri = templateListUri();
+ logger.debug("Retrieving all SLO/SLE templates");
+ try {
+ ResponseEntity resp = restTemplate.exchange(
+ uri, HttpMethod.GET,
+ new HttpEntity<>(buildHeaders()),
+ String.class);
+ assertSuccess(resp, "get SLO/SLE templates");
+ String body = resp.getBody();
+ return (body == null || body.isBlank()) ? "{}" : body;
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (RestconfException e) {
+ throw e;
+ } catch (Exception e) {
+ throw wrap("get SLO/SLE templates", e);
+ }
+ }
+
+ @Override
+ public void deleteAllSloSleTemplates() throws RestconfException {
+ String uri = templateListUri();
+ logger.info("Deleting all SLO/SLE templates");
+ try {
+ restTemplate.exchange(uri, HttpMethod.DELETE,
+ new HttpEntity<>(buildHeaders()), Void.class);
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (Exception e) {
+ throw wrap("delete all SLO/SLE templates", e);
+ }
+ }
+
+ @Override
+ public SloSleTemplate getSloSleTemplate(String templateId) throws RestconfException {
+ String uri = templateUri(templateId);
+ logger.debug("Retrieving SLO/SLE template: {}", templateId);
+ try {
+ ResponseEntity resp = restTemplate.exchange(
+ uri, HttpMethod.GET,
+ new HttpEntity<>(buildHeaders()),
+ SloSleTemplate.class);
+ assertSuccess(resp, "get SLO/SLE template " + templateId);
+ return resp.getBody();
+ } catch (HttpClientErrorException.NotFound e) {
+ throw notFound("SLO/SLE template", templateId);
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (RestconfException e) {
+ throw e;
+ } catch (Exception e) {
+ throw wrap("get SLO/SLE template " + templateId, e);
+ }
+ }
+
+ @Override
+ public SloSleTemplate updateSloSleTemplate(String templateId, SloSleTemplate template)
+ throws RestconfException {
+ String uri = templateUri(templateId);
+ logger.info("Updating SLO/SLE template: {}", templateId);
+ try {
+ ResponseEntity resp = restTemplate.exchange(
+ uri, HttpMethod.PUT,
+ new HttpEntity<>(template, buildHeaders()),
+ SloSleTemplate.class);
+ assertSuccess(resp, "update SLO/SLE template " + templateId);
+ return resp.getBody();
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (RestconfException e) {
+ throw e;
+ } catch (Exception e) {
+ throw wrap("update SLO/SLE template " + templateId, e);
+ }
+ }
+
+ @Override
+ public void deleteSloSleTemplate(String templateId) throws RestconfException {
+ String uri = templateUri(templateId);
+ logger.info("Deleting SLO/SLE template: {}", templateId);
+ try {
+ restTemplate.exchange(uri, HttpMethod.DELETE,
+ new HttpEntity<>(buildHeaders()), Void.class);
+ } catch (HttpClientErrorException.NotFound e) {
+ throw notFound("SLO/SLE template", templateId);
+ } catch (HttpClientErrorException e) {
+ throw handleHttpError(e);
+ } catch (Exception e) {
+ throw wrap("delete SLO/SLE template " + templateId, e);
+ }
+ }
+
+ // =========================================================================
+ // RFC 9543 payload builder
+ // =========================================================================
+
+ /**
+ * Builds the RFC 9543 RESTCONF JSON payload for a NetworkSliceServices container.
+ *
+ * Produces the standard RFC 9543 wire format:
+ *
+ */
+public interface LogicalResourceMappable {
+
+ /**
+ * Get the entity type name for resource category.
+ * Default implementation returns the class simple name.
+ * Can be overridden for custom naming.
+ *
+ * Examples: "Datacenter", "EdgeSite", "ComputeDevice", "Region"
+ *
+ * @return Entity type name
+ */
+ @JsonIgnore
+ default String getEntityTypeName() {
+ return this.getClass().getSimpleName();
+ }
+
+ /**
+ * Get the unique identifier for this entity.
+ * Used as LogicalResource.uuid.
+ *
+ * @return Entity ID (UUID string)
+ */
+ String getEntityId();
+
+ /**
+ * Get the display name for this entity.
+ * Used as LogicalResource.name.
+ *
+ * @return Entity name
+ */
+ String getEntityName();
+
+ /**
+ * Get the description for this entity.
+ * Used as LogicalResource.description.
+ * Can return null or empty string - mapper handles default descriptions.
+ *
+ * @return Entity description or null
+ */
+ String getEntityDescription();
+
+ /**
+ * Get the version constant for this entity type.
+ * Used in resource specification reference versioning.
+ *
+ * Default implementation uses reflection to retrieve the VERSION field.
+ * Can be overridden for performance optimization.
+ *
+ * @return Version string (e.g., "0.0.1")
+ */
+ @JsonIgnore
+ default String getVersion() {
+ try {
+ Class> clazz = this.getClass();
+ // For subclasses, check superclass hierarchy
+ while (clazz != null && !clazz.equals(Object.class)) {
+ try {
+ Field versionField = clazz.getDeclaredField("VERSION");
+ if (versionField != null) {
+ versionField.setAccessible(true);
+ Object version = versionField.get(null);
+ return version != null ? version.toString() : "0.0.1";
+ }
+ } catch (NoSuchFieldException e) {
+ // Try superclass
+ clazz = clazz.getSuperclass();
+ }
+ }
+ } catch (Exception e) {
+ // Silently fall back to default
+ }
+ return "0.0.1";
+ }
+
+ /**
+ * Check if this entity type should have status mapping.
+ * Override in entities with status fields to enable status mapping.
+ *
+ * @return true if entity has status that should be mapped
+ */
+ @JsonIgnore
+ default boolean hasStatusMapping() {
+ return false;
+ }
+
+ /**
+ * Check if this entity has child physical devices.
+ * Override in Site subclasses to enable device relationship mapping.
+ *
+ * @return true if entity can have deployed physical devices
+ */
+ @JsonIgnore
+ default boolean hasPhysicalDevices() {
+ return false;
+ }
+
+ /**
+ * Map entity status to TMF639 resource states.
+ * Override in entities that have status fields (Site, Region, PhysicalDevice).
+ * Default implementation does nothing - only entities with status should override.
+ *
+ * Implementations should set TMF resource state fields:
+ * - administrativeState
+ * - operationalState
+ * - resourceStatus
+ * - usageState
+ *
+ * @param resource LogicalResource to populate with status-based states
+ */
+ @JsonIgnore
+ default void mapStatusToResourceStates(LogicalResource resource) {
+ // Default: no-op. Override in entities with status.
+ }
+
+ /**
+ * Map entity administrative state to TMF639 resource administrative state.
+ * Override in PhysicalDevice class for device-specific administrative state mapping.
+ * Default implementation does nothing - only PhysicalDevice should override.
+ *
+ * Implementations should set TMF administrativeState field based on entity's administrative state.
+ *
+ * @param resource LogicalResource to populate with administrative state
+ */
+ @JsonIgnore
+ default void mapAdministrativeStateToResource(LogicalResource resource) {
+ // Default: no-op. Override in PhysicalDevice.
+ }
+
+ /**
+ * Map fields from a TMF639 LogicalResource back to this entity instance.
+ * Override in entities to populate fields from a LogicalResource during reverse mapping.
+ * Default implementation does nothing - only entities with reverse mapping should override.
+ *
+ * Implementations should extract characteristics and relationships from the resource
+ * and populate the corresponding entity fields.
+ *
+ * Called during deserialization: LogicalResource → Entity
+ *
+ * @param resource LogicalResource containing characteristics and relationships
+ * @param characteristicMap Map of characteristic names to values (helper for field extraction)
+ */
+ @JsonIgnore
+ default void mapFromLogicalResource(LogicalResource resource, java.util.Map characteristicMap) {
+ // Default: no-op. Override in entities with reverse mapping.
+ }
+
+ /**
+ * Map TMF639 resource states back to entity status/state fields.
+ * Override in entities that have status fields (Site, Region, PhysicalDevice).
+ * Default implementation does nothing - only entities with status should override.
+ *
+ * Implementations should extract TMF resource state fields and apply them to the entity:
+ * - administrativeState → entity administrative state if applicable
+ * - operationalState → compute entity status
+ * - resourceStatus → compute entity status
+ * - usageState → compute entity status
+ *
+ * @param resource LogicalResource containing state fields
+ */
+ @JsonIgnore
+ default void applyResourceStates(LogicalResource resource) {
+ // Default: no-op. Override in entities with status.
+ }
+
+ // ========== Reverse Mapping Support (LogicalResource → Entity) ==========
+
+ /**
+ * Get field mappings for reverse mapping (LogicalResource characteristics → entity properties).
+ * Maps TMF characteristic field names to entity property names and types.
+ *
+ * Format: Maps characteristic field names (e.g., "rackCount", "totalPowerCapacity") to entity property names.
+ *
+ *
+ * Override in concrete entity classes to provide type-specific field mappings.
+ * Subclasses should call super.getFieldMappings() and add their own fields.
+ *
+ * @return Map of characteristic field names to entity property names (empty by default)
+ */
+ @JsonIgnore
+ default Map getFieldMappings() {
+ return Map.of();
+ }
+
+ /**
+ * Check if this entity has relationships that need extraction from LogicalResource.
+ * Override in entities that have entity references
+
+ *
+ * @return true if entity has relationships to extract from LogicalResource
+ */
+ @JsonIgnore
+ default boolean hasRelationships() {
+ return false;
+ }
+
+ /**
+ * Get the relationship field names for this entity.
+ * Used during reverse mapping to extract and populate entity references from LogicalResource.
+ *
+ * Format: List of relationship field names as defined in entity class.
+
+ *
+ * Only override if hasRelationships() returns true.
+ * Should return the relationship field names as they appear in the entity class.
+ *
+ * @return List of relationship field names (empty by default)
+ */
+ @JsonIgnore
+ default List getRelationshipFields() {
+ return List.of();
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/domain/common/LogicalResourceSpecMappable.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/domain/common/LogicalResourceSpecMappable.java
new file mode 100644
index 0000000000000000000000000000000000000000..51cd907ad508cc28958f51f3813c58d7b1bb70ba
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/domain/common/LogicalResourceSpecMappable.java
@@ -0,0 +1,193 @@
+package org.etsi.osl.controllers.ietf.ns.domain.common;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.etsi.osl.tmf.rcm634.model.LogicalResourceSpecification;
+
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Interface for entities that can be mapped to/from TMF634 LogicalResourceSpecification instances.
+ * Provides a contract for both forward (entity → resourceSpec) and reverse (resourceSpec → entity) mapping operations.
+ *
+ * This interface is for CATALOG/DESIGN-TIME entities (controllers, operator definitions, etc.)
+ * that define types and specifications, NOT runtime instances.
+ *
+ * Contrast with {@link LogicalResourceMappable} which is for runtime/inventory entities (TMF639).
+ *
+ * Implementations should:
+ * 1. Provide entity ID, name, and description via abstract methods
+ * 2. Define a VERSION constant for schema versioning
+ * 3. Be annotated with @ExcludeFromEntityToLogicalResourceCharacteristicsMapping for fields not meant for mapping
+ * 4. Override getFieldMappings() to declare characteristic-to-property mappings
+ * 5. Override hasRelationships() and getRelationshipFields() if entity has relationships
+ *
+ * Usage:
+ *
+ *
+ * Override in concrete entity classes to provide type-specific field mappings.
+ * Subclasses should call super.getFieldMappings() and add their own fields.
+ *
+ * @return Map of characteristic field names to entity property names (empty by default)
+ */
+ @JsonIgnore
+ default Map getFieldMappings() {
+ return Map.of();
+ }
+
+ /**
+ * Check if this entity has relationships that need extraction from LogicalResourceSpecification.
+ * Override in entities that have entity references (e.g., GenericController has kubernetesOperators).
+ *
+ * Examples:
+ * - GenericController: has "kubernetesOperators" relationship
+ * - KubernetesOperator: has "controller" relationship
+ *
+ * @return true if entity has relationships to extract from LogicalResourceSpecification
+ */
+ @JsonIgnore
+ default boolean hasRelationships() {
+ return false;
+ }
+
+ /**
+ * Get the relationship field names for this entity.
+ * Used during reverse mapping to extract and populate entity references from LogicalResourceSpecification.
+ *
+ * Format: List of relationship field names as defined in entity class.
+ *
+ * Examples:
+ * - GenericController: List.of("kubernetesOperators")
+ * - KubernetesOperator: List.of("controller")
+ *
+ * Only override if hasRelationships() returns true.
+ * Should return the relationship field names as they appear in the entity class.
+ *
+ * @return List of relationship field names (empty by default)
+ */
+ @JsonIgnore
+ default List getRelationshipFields() {
+ return List.of();
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/domain/common/RelatedManagedResourceReference.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/domain/common/RelatedManagedResourceReference.java
new file mode 100644
index 0000000000000000000000000000000000000000..22e85d2006e4ebff2f2b627b46400c1352684b0f
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/domain/common/RelatedManagedResourceReference.java
@@ -0,0 +1,67 @@
+package org.etsi.osl.controllers.ietf.ns.domain.common;
+
+import org.etsi.osl.tmf.ri639.model.LogicalResource;
+
+/**
+ * Interface for entities that can have related managed resources.
+ * Provides a contract for entities that need to track relationships to other managed resources
+ * from the TMF Resource Inventory.
+ *
+ * Implementations should:
+ * 1. Maintain a relatedManagedResourceId field (writable by clients)
+ * 2. Maintain a relatedManagedResource field (read-only, service-managed)
+ * 3. Implement getter/setter methods for both fields
+ *
+ * Usage:
+ *
+ * // Client sets the ID
+ * device.setRelatedManagedResourceId("resource-uuid-123");
+ *
+ * // Service layer fetches and populates the resource
+ * mapper.setRelatedManagedResource(device);
+ *
+ * // Later retrieval includes the populated resource
+ * LogicalResource relatedResource = device.getRelatedManagedResource();
+ *
+ *
+ * Examples of implementing classes:
+ * - PhysicalDevice - Links devices to related managed resources (clusters, controllers, etc.)
+ * - ComputeDomain - Links to related resource that describes the compute domain
+ * - Future: Any entity that needs to reference other managed resources
+ */
+public interface RelatedManagedResourceReference {
+
+ /**
+ * Get the ID of the related managed resource.
+ * This is the writable field that clients can set to establish relationships.
+ *
+ * @return Related managed resource ID (UUID string), or null if not set
+ */
+ String getRelatedManagedResourceId();
+
+ /**
+ * Set the ID of the related managed resource.
+ * Clients can use this to establish relationships to other managed resources.
+ *
+ * @param relatedManagedResourceId Related managed resource ID (UUID string)
+ */
+ void setRelatedManagedResourceId(String relatedManagedResourceId);
+
+ /**
+ * Get the related managed resource fetched from the inventory.
+ * This field is read-only and service-managed - it contains the actual LogicalResource
+ * object fetched from the TMF Resource Inventory based on relatedManagedResourceId.
+ *
+ * @return LogicalResource object, or null if not yet fetched/set
+ */
+ LogicalResource getRelatedManagedResource();
+
+ /**
+ * Set the related managed resource fetched from the inventory.
+ * This is called by the service layer after fetching the resource from inventory.
+ * Clients cannot set this field directly (it's marked read-only in JSON).
+ *
+ * @param relatedManagedResource LogicalResource object fetched from inventory
+ */
+ void setRelatedManagedResource(LogicalResource relatedManagedResource);
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/mappers/EntityToLogicalResourceMapper.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/mappers/EntityToLogicalResourceMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..8e36eb92090382ad707b9ba6bbbd5c432f5c3f2b
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/mappers/EntityToLogicalResourceMapper.java
@@ -0,0 +1,462 @@
+package org.etsi.osl.controllers.ietf.ns.mappers;
+
+import java.lang.reflect.Field;
+import java.time.LocalDateTime;
+import java.util.Collection;
+import org.etsi.osl.controllers.ietf.ns.api.CategoryConfigurationService;
+import org.etsi.osl.controllers.ietf.ns.api.ResourceSpecificationTemplateRegistry;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.SloSleTemplate;
+import org.etsi.osl.controllers.ietf.ns.domain.common.ExcludeFromMapping;
+import org.etsi.osl.controllers.ietf.ns.domain.common.LogicalResourceMappable;
+import org.etsi.osl.tmf.common.model.Any;
+import org.etsi.osl.tmf.common.model.service.ResourceRef;
+import org.etsi.osl.tmf.rcm634.model.ResourceSpecificationRef;
+import org.etsi.osl.tmf.ri639.model.Characteristic;
+import org.etsi.osl.tmf.ri639.model.LogicalResource;
+import org.etsi.osl.tmf.ri639.model.ResourceRelationship;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import jakarta.persistence.Transient;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Mapper utility to transform domain entities to TMF639 LogicalResource instances
+ * for persistence in Resource Inventory.
+ *
+ * This refactored version uses a single generic method for all LogicalResourceMappable
+ * entities, reducing code duplication and improving maintainability.
+ *
+ * Strategy:
+ * - Use entity name as LogicalResource name
+ * - Use category from CategoryConfigurationService with entity type suffix
+ * - Reference the template ID from ResourceSpecificationTemplateRegistry
+ * - Convert all simple fields to Characteristic
+ * - Convert entity references to ResourceRelationship
+ * - Map entity status (if present) to TMF resource states
+ */
+@Component
+@Slf4j
+public class EntityToLogicalResourceMapper {
+
+ private static final String RELATIONSHIP_TYPE_DEPENDENCY = "dependency";
+
+ @Autowired
+ private CategoryConfigurationService categoryConfig;
+
+ @Autowired
+ private ResourceSpecificationTemplateRegistry templateRegistry;
+
+ /**
+ * Generic mapping method for any LogicalResourceMappable entity.
+ * This single method replaces all the previous overloaded toLogicalResource() methods.
+ *
+ * @param entity The entity to map (must implement LogicalResourceMappable)
+ * @return LogicalResource instance ready for persistence
+ * @throws RuntimeException if entity type template not found in registry
+ * @throws IllegalArgumentException if entity is null
+ */
+ public LogicalResource toLogicalResource(LogicalResourceMappable entity) {
+ if (entity == null) {
+ throw new IllegalArgumentException("Entity cannot be null");
+ }
+
+ log.debug("Mapping {} to LogicalResource", entity.getEntityTypeName());
+
+ LogicalResource resource = new LogicalResource();
+ String entityType = entity.getEntityTypeName();
+
+ // Map basic attributes
+ resource.setName(entity.getEntityName());
+ resource.setCategory(categoryConfig.getCategoryForEntity(entityType));
+ resource.setDescription(entity.getEntityDescription());
+
+ // Set ID if entity has one
+ if (entity.getEntityId() != null) {
+ resource.setUuid(entity.getEntityId());
+ }
+
+ // Reference the resource specification template
+ setResourceSpecificationReference(resource, entityType, entity.getVersion());
+
+ // Handle status mapping via entity methods
+ if (entity.hasStatusMapping()) {
+ entity.mapStatusToResourceStates(resource);
+ }
+
+ // Convert all fields to characteristics and relationships
+ convertFieldsToCharacteristicsAndRelationships(entity, resource);
+
+
+
+ return resource;
+ }
+
+ /**
+ * Set the resource specification reference to the template registered during bootstrap
+ */
+ private void setResourceSpecificationReference(LogicalResource resource,
+ String entityType,
+ String version) {
+ String templateId = templateRegistry.getTemplateId(entityType)
+ .orElseThrow(() -> new RuntimeException(
+ entityType + " template not registered. Bootstrap may have failed."));
+
+ ResourceSpecificationRef specRef = new ResourceSpecificationRef();
+ specRef.setId(templateId);
+ specRef.setName(entityType);
+ specRef.setVersion(version);
+ specRef.setReferredType("LogicalResourceSpecification");
+
+ resource.setResourceSpecification(specRef);
+ log.debug("Set resourceSpecification reference to template ID: {}", templateId);
+ }
+ /**
+ * Check if a class is a value object (embedded type)
+ */
+ private boolean isValueObject(Class> type) {
+ return false;
+ }
+
+
+ /**
+ * Convert all fields of an entity to characteristics and relationships
+ * Uses @ExcludeFromMapping annotation to skip fields
+ */
+ private void convertFieldsToCharacteristicsAndRelationships(Object entity, LogicalResource resource) {
+ Class> clazz = entity.getClass();
+
+ // Process fields from the entity class and its superclasses
+ while (clazz != null && !clazz.equals(Object.class)) {
+ for (Field field : clazz.getDeclaredFields()) {
+ try {
+ // Check if field is excluded from mapping
+ if (field.isAnnotationPresent(ExcludeFromMapping.class)) {
+ ExcludeFromMapping exclusion = field.getAnnotation(ExcludeFromMapping.class);
+ if (exclusion.logIfSkipped()) {
+ log.debug("Skipping field {} - {}", field.getName(), exclusion.reason());
+ }
+ continue;
+ }
+
+ field.setAccessible(true);
+ Object value = field.get(entity);
+
+ // Skip static fields and @Transient fields
+ if (java.lang.reflect.Modifier.isStatic(field.getModifiers())
+ || field.isAnnotationPresent(Transient.class)) {
+ continue;
+ }
+
+ String fieldName = field.getName();
+ Class> fieldType = field.getType();
+
+ // Handle relatedManagedResourceId as a ResourceRelationship instead of a characteristic
+ if ("relatedManagedResourceId".equals(fieldName) && value != null && value instanceof String) {
+ addManagedResourceRelationship(resource, (String) value);
+ continue;
+ }
+
+ // Handle entity relationships
+ if (isEntityType(fieldType)) {
+ if (value != null) {
+ addRelationship(resource, fieldName, fieldType, value);
+ }
+ }
+ // Handle collections - pass the field to check generic type
+ else if (Collection.class.isAssignableFrom(fieldType)) {
+ handleCollectionField(resource, fieldName, field, value);
+ }
+ // Handle value objects (embedded types)
+ else if (isValueObject(fieldType)) {
+ addValueObjectCharacteristics(resource, fieldName, value);
+ }
+ // Handle simple types as characteristics
+ else {
+ addCharacteristic(resource, fieldName, value, fieldType);
+ }
+
+ } catch (IllegalAccessException e) {
+ log.warn("Could not access field {} in class {}", field.getName(), clazz.getSimpleName(), e);
+ }
+ }
+ clazz = clazz.getSuperclass();
+ }
+ }
+
+ /**
+ * Check if a class is an entity type
+ */
+ private boolean isEntityType(Class> type) {
+ return SloSleTemplate.class.isAssignableFrom(type);
+ }
+
+
+ /**
+ * Handle collection fields - distinguish between simple types and entities
+ */
+ private void handleCollectionField(LogicalResource resource, String fieldName, Field field, Object value) {
+ Collection> collection = (Collection>) value;
+
+ // Determine if collection holds entities by examining generic type parameter
+ String entityTypeName = getCollectionEntityType(field);
+
+ if (collection.isEmpty()) {
+ if (entityTypeName != null) {
+ log.debug("Empty collection of {} entities for field: {} - no relationships to add",
+ entityTypeName, fieldName);
+ }
+ addArrayCharacteristic(resource, fieldName, "[]");
+ return;
+ }
+
+ if (entityTypeName != null) {
+ // Collection of entities → Create a relationship for each entity
+ log.debug("Handling collection of {} {} entities for field: {}",
+ collection.size(), entityTypeName, fieldName);
+ for (Object element : collection) {
+ addRelationship(resource, fieldName, element.getClass(), element);
+ }
+ } else {
+ // Collection of simple types → Single ARRAY characteristic
+ log.debug("Handling collection of {} simple types for field: {}", collection.size(), fieldName);
+ addArrayCharacteristic(resource, fieldName, convertCollectionToString(collection));
+ }
+ }
+
+ /**
+ * Get the entity type name from a collection field using reflection
+ */
+ private String getCollectionEntityType(Field field) {
+ try {
+ java.lang.reflect.Type genericType = field.getGenericType();
+
+ if (genericType instanceof java.lang.reflect.ParameterizedType) {
+ java.lang.reflect.ParameterizedType paramType = (java.lang.reflect.ParameterizedType) genericType;
+ java.lang.reflect.Type[] typeArgs = paramType.getActualTypeArguments();
+
+ if (typeArgs.length > 0 && typeArgs[0] instanceof Class) {
+ Class> elementType = (Class>) typeArgs[0];
+
+ if (isEntityType(elementType)) {
+ return elementType.getSimpleName();
+ }
+ }
+ }
+ } catch (Exception e) {
+ log.warn("Could not determine generic type for field {}: {}", field.getName(), e.getMessage());
+ }
+
+ return null;
+ }
+
+ /**
+ * Add an ARRAY characteristic for collections
+ */
+ private void addArrayCharacteristic(LogicalResource resource, String fieldName, String arrayValue) {
+ Characteristic characteristic = new Characteristic();
+ characteristic.setName(fieldName);
+ characteristic.setValueType("ARRAY");
+
+ Any anyValue = new Any();
+ anyValue.setValue(arrayValue);
+ anyValue.setAlias(fieldName);
+ characteristic.setValue(anyValue);
+
+ resource.addResourceCharacteristicItem(characteristic);
+ log.debug("Added ARRAY characteristic: {} = {}", fieldName, arrayValue);
+ }
+
+ /**
+ * Convert collection to string representation
+ */
+ private String convertCollectionToString(Collection> collection) {
+ if (collection == null || collection.isEmpty()) {
+ return "[]";
+ }
+
+ StringBuilder sb = new StringBuilder("[");
+ boolean first = true;
+ for (Object item : collection) {
+ if (!first) {
+ sb.append(", ");
+ }
+ if (item instanceof String) {
+ sb.append("\"").append(item).append("\"");
+ } else {
+ sb.append(item);
+ }
+ first = false;
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ /**
+ * Add a characteristic for a simple field
+ */
+ private void addCharacteristic(LogicalResource resource, String fieldName, Object value, Class> type) {
+ Characteristic characteristic = new Characteristic();
+ characteristic.setName(fieldName);
+ characteristic.setValueType(getValueType(type));
+
+ Any anyValue = new Any();
+ anyValue.setValue(convertValueToString(value));
+ anyValue.setAlias(fieldName);
+ characteristic.setValue(anyValue);
+
+ resource.addResourceCharacteristicItem(characteristic);
+ log.debug("Added characteristic: {} = {}", fieldName, value);
+ }
+
+ /**
+ * Add characteristics for value object fields
+ */
+ private void addValueObjectCharacteristics(LogicalResource resource, String fieldName, Object valueObject) {
+ if (valueObject == null) {
+ return;
+ }
+
+ Class> voClass = valueObject.getClass();
+
+ for (Field voField : voClass.getDeclaredFields()) {
+ try {
+ voField.setAccessible(true);
+ Object voValue = voField.get(valueObject);
+
+ if (voValue != null && !java.lang.reflect.Modifier.isStatic(voField.getModifiers())) {
+ String nestedFieldName = fieldName + "." + voField.getName();
+ addCharacteristic(resource, nestedFieldName, voValue, voField.getType());
+ }
+ } catch (IllegalAccessException e) {
+ log.warn("Could not access field {} in value object {}", voField.getName(), voClass.getSimpleName(), e);
+ }
+ }
+ }
+
+ /**
+ * Add a ResourceRelationship for an entity reference
+ */
+ private void addRelationship(LogicalResource resource, String fieldName, Class> relatedClass, Object relatedEntity) {
+ ResourceRelationship relationship = new ResourceRelationship();
+
+ // Get the ID of the related entity
+ String relatedId = extractEntityId(relatedEntity);
+
+ // Get the actual entity type name (use actual object class for polymorphic types)
+ String entityType = relatedEntity.getClass().getSimpleName();
+
+ ResourceRef resourceRef = new ResourceRef();
+ resourceRef.setId(relatedId);
+ resourceRef.setName(fieldName);
+ resourceRef.setReferredType(entityType);
+
+ if (relatedId != null) {
+ resourceRef.setHref("/api/topology/" + entityType.toLowerCase() + "s/" + relatedId);
+ }
+
+ relationship.setResource(resourceRef);
+ relationship.setRelationshipType(RELATIONSHIP_TYPE_DEPENDENCY);
+
+ resource.addResourceRelationshipItem(relationship);
+ log.debug("Added ResourceRelationship: field={}, referredType={}, id={}", fieldName, entityType, relatedId);
+ }
+
+ /**
+ * Add a ResourceRelationship for a related managed resource reference
+ */
+ private void addManagedResourceRelationship(LogicalResource resource, String relatedManagedResourceId) {
+ if (relatedManagedResourceId == null || relatedManagedResourceId.isEmpty()) {
+ return;
+ }
+
+ ResourceRelationship relationship = new ResourceRelationship();
+
+ ResourceRef resourceRef = new ResourceRef();
+ resourceRef.setId(relatedManagedResourceId);
+ resourceRef.setName("relatedManagedResource");
+ resourceRef.setReferredType("LogicalResource");
+ resourceRef.setHref("/api/inventory/resources/" + relatedManagedResourceId);
+
+ relationship.setResource(resourceRef);
+ relationship.setRelationshipType(RELATIONSHIP_TYPE_DEPENDENCY);
+
+ resource.addResourceRelationshipItem(relationship);
+ log.debug("Added managed resource relationship: id={}, referredType=LogicalResource", relatedManagedResourceId);
+ }
+
+ /**
+ * Extract ID from an entity using reflection
+ */
+ private String extractEntityId(Object entity) {
+ try {
+ Field idField = findIdField(entity.getClass());
+ if (idField != null) {
+ idField.setAccessible(true);
+ Object id = idField.get(entity);
+ return id != null ? id.toString() : null;
+ }
+ } catch (IllegalAccessException e) {
+ log.warn("Could not extract ID from entity {}", entity.getClass().getSimpleName(), e);
+ }
+ return null;
+ }
+
+ /**
+ * Find the ID field in a class hierarchy
+ */
+ private Field findIdField(Class> clazz) {
+ while (clazz != null && !clazz.equals(Object.class)) {
+ for (Field field : clazz.getDeclaredFields()) {
+ if ("id".equals(field.getName()) || "siteId".equals(field.getName())) {
+ return field;
+ }
+ }
+ clazz = clazz.getSuperclass();
+ }
+ return null;
+ }
+
+ /**
+ * Get TMF value type from Java type
+ */
+ private String getValueType(Class> type) {
+ if (type.equals(String.class)) {
+ return "TEXT";
+ } else if (type.equals(Integer.class) || type.equals(int.class) ||
+ type.equals(Long.class) || type.equals(long.class)) {
+ return "NUMBER";
+ } else if (type.equals(Boolean.class) || type.equals(boolean.class)) {
+ return "BOOLEAN";
+ } else if (type.equals(Double.class) || type.equals(double.class) ||
+ type.equals(Float.class) || type.equals(float.class)) {
+ return "NUMBER";
+ } else if (type.isEnum()) {
+ return "TEXT";
+ } else if (LocalDateTime.class.isAssignableFrom(type)) {
+ return "DATETIME";
+ } else if (Collection.class.isAssignableFrom(type)) {
+ return "ARRAY";
+ } else {
+ return "TEXT";
+ }
+ }
+
+ /**
+ * Convert value to string representation
+ */
+ private String convertValueToString(Object value) {
+ if (value == null) {
+ return "";
+ }
+
+ if (value instanceof Collection) {
+ return convertCollectionToString((Collection>) value);
+ } else if (value instanceof LocalDateTime) {
+ return value.toString();
+ } else if (value.getClass().isEnum()) {
+ return ((Enum>) value).name();
+ } else {
+ return value.toString();
+ }
+ }
+}
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/mappers/EntityToLogicalResourceSpecMapper.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/mappers/EntityToLogicalResourceSpecMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..68c50ffd1557897b9381279b9796f49e1d56169f
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/mappers/EntityToLogicalResourceSpecMapper.java
@@ -0,0 +1,529 @@
+package org.etsi.osl.controllers.ietf.ns.mappers;
+
+import java.lang.reflect.Field;
+import java.time.LocalDateTime;
+import java.util.Collection;
+import java.util.Set;
+import org.etsi.osl.controllers.ietf.ns.api.CategoryConfigurationService;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.SloSleTemplate;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.AvailabilityType;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.MetricBound;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.PathConstraints;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.SlePolicy;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.slo_sle.SloPolicy;
+import org.etsi.osl.controllers.ietf.ns.domain.common.LogicalResourceMappable;
+import org.etsi.osl.controllers.ietf.ns.domain.common.LogicalResourceSpecMappable;
+import org.etsi.osl.tmf.common.model.Any;
+import org.etsi.osl.tmf.pm628.model.AdministrativeState;
+import org.etsi.osl.tmf.rcm634.model.LogicalResourceSpecification;
+import org.etsi.osl.tmf.rcm634.model.ResourceSpecificationCharacteristic;
+import org.etsi.osl.tmf.rcm634.model.ResourceSpecificationCharacteristicValue;
+import org.etsi.osl.tmf.rcm634.model.ResourceSpecificationRelationship;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Mapper utility to transform domain topology entities to TMF LogicalResourceSpecification
+ * Strategy:
+ * - Use class name as LogicalResourceSpecification name
+ * - Use category from CategoryConfigurationService
+ * - Use class VERSION constant as version
+ * - Convert all simple fields to ResourceSpecificationCharacteristic
+ * - Convert entity references to ResourceSpecificationRelationship
+ */
+@Component
+@Slf4j
+public class EntityToLogicalResourceSpecMapper {
+
+ private static final String RELATIONSHIP_TYPE_DEPENDENCY = "dependency";
+
+ @Autowired
+ private CategoryConfigurationService categoryConfig;
+
+ // ========== Type Registries ==========
+
+ /**
+ * Registry of all entity types (for example, topology sites, physical devices, etc)
+ * Used for efficient type checking without long chains of instanceof
+ */
+ private static final Set> ENTITY_TYPES = Set.of(
+ SloSleTemplate.class,
+ MetricBound.class
+ );
+
+ /**
+ * Registry of all value object types (embedded types)
+ * Used for identifying and processing value objects
+ */
+ private static final Set> VALUE_OBJECT_TYPES = Set.of(
+ SloPolicy.class,
+ SlePolicy.class,
+ AvailabilityType.class,
+ PathConstraints.class
+ );
+
+ /**
+ * Transform an entity to LogicalResourceSpecification (generic method).
+ *
+ * @param Entity type extending LogicalResourceMappable
+ * @param entity Entity to convert
+ * @return LogicalResourceSpecification representing the entity
+ */
+ public LogicalResourceSpecification toLogicalResourceSpec(Object entity) {
+ // Validate that entity implements one of the mappable interfaces
+ if (!(entity instanceof LogicalResourceMappable) && !(entity instanceof LogicalResourceSpecMappable)) {
+ throw new IllegalArgumentException("Entity must implement LogicalResourceMappable or LogicalResourceSpecMappable");
+ }
+
+ String entityTypeName = getEntityTypeName(entity);
+ String entityName = getEntityName(entity);
+ log.debug("Mapping {} to LogicalResourceSpecification", entityName);
+
+ LogicalResourceSpecification spec = new LogicalResourceSpecification();
+
+ // Set basic attributes using interface methods
+ spec.setName(entityName);
+ spec.setCategory(categoryConfig.getCategoryForSpecifications());
+ spec.setVersion(getVersion(entity));
+ // Use the raw description field value (may be null), not the interface method which provides defaults
+
+ spec.setDescription( getEntityDescription( entity));
+ spec.setLifecycleStatus("Active");
+
+ // Set ID if entity has one
+ String entityId = getEntityId(entity);
+ if (entityId != null) {
+ spec.setUuid(entityId);
+ }
+
+ // Convert all fields to characteristics and relationships
+ convertFieldsToCharacteristicsAndRelationships(entity, spec);
+
+
+ return spec;
+ }
+
+
+
+
+
+
+
+ /**
+ * Convert all fields of an entity to characteristics and relationships
+ */
+ private void convertFieldsToCharacteristicsAndRelationships(Object entity, LogicalResourceSpecification spec) {
+ Class> clazz = entity.getClass();
+
+ // Process fields from the entity class and its superclasses
+ while (clazz != null && !clazz.equals(Object.class)) {
+ for (Field field : clazz.getDeclaredFields()) {
+ try {
+ field.setAccessible(true);
+ Object value = field.get(entity);
+
+ // Skip static fields, VERSION, and lifecycle fields
+ // These are metadata that shouldn't be in the specification
+ if (java.lang.reflect.Modifier.isStatic(field.getModifiers())
+ || "VERSION".equals(field.getName())
+ || "id".equals(field.getName())
+ || "siteId".equals(field.getName())
+ || "name".equals(field.getName())
+ || "description".equals(field.getName())
+ || "createdAt".equals(field.getName())
+ || "lastModified".equals(field.getName())) {
+ continue;
+ }
+
+ String fieldName = field.getName();
+ Class> fieldType = field.getType();
+
+ // Handle entity relationships
+ if (isEntityType(fieldType)) {
+ //addRelationship(spec, fieldName, fieldType, value);
+ addCharacteristic(spec, fieldName, value, fieldType);
+ }
+ // Handle collections - pass the field to check generic type
+ else if (Collection.class.isAssignableFrom(fieldType)) {
+ handleCollectionField(spec, fieldName, field, value);
+ }
+ // Handle value objects (embedded types)
+ else if (isValueObject(fieldType) && value!=null) {
+ addValueObjectCharacteristics(spec, fieldName, value);
+ }
+ // Handle simple types as characteristics
+ else {
+ addCharacteristic(spec, fieldName, value, fieldType);
+ }
+
+ } catch (IllegalAccessException e) {
+ log.warn("Could not access field {} in class {}", field.getName(), clazz.getSimpleName(), e);
+ }
+ }
+ clazz = clazz.getSuperclass();
+ }
+ }
+
+ /**
+ * Check if a class is an entity type using registry
+ */
+ private boolean isEntityType(Class> type) {
+ return ENTITY_TYPES.stream().anyMatch(t -> t.isAssignableFrom(type));
+ }
+
+ /**
+ * Check if a class is a value object (embedded type) using registry
+ */
+ private boolean isValueObject(Class> type) {
+ return VALUE_OBJECT_TYPES.contains(type);
+ }
+
+ /**
+ * Handle collection fields - distinguish between simple types and entities
+ * - Collections of simple types (List, List, etc.) → ARRAY characteristic
+ * - Collections of entities (List, etc.) → Multiple relationships
+ *
+ * Uses reflection to check the generic type parameter, so it works even for empty collections
+ */
+ private void handleCollectionField(LogicalResourceSpecification spec, String fieldName, Field field, Object value) {
+ Collection> collection = (Collection>) value;
+
+ // Determine if collection holds entities by examining generic type parameter
+ // Returns entity type name (e.g., "EdgeSite") or null if not an entity collection
+ String entityTypeName = getCollectionEntityType(field);
+
+ if (collection.isEmpty()) {
+ if (entityTypeName != null) {
+ // Empty collection of entities → No relationships to add, just log
+ log.debug("Empty collection of {} entities for field: {} - no relationships to add",
+ entityTypeName, fieldName);
+ addArrayCharacteristic(spec, fieldName, "[]");
+ } else {
+ // Empty collection of simple types → Add empty ARRAY characteristic
+ addArrayCharacteristic(spec, fieldName, "[]");
+ log.debug("Added empty ARRAY characteristic for field: {}", fieldName);
+ }
+ return;
+ }
+
+ if (entityTypeName != null) {
+ // Collection of entities → Create a relationship for each entity
+ log.debug("Handling collection of {} {} entities for field: {}",
+ collection.size(), entityTypeName, fieldName);
+ for (Object element : collection) {
+ addRelationship(spec, fieldName, element.getClass(), element);
+ }
+ } else {
+ // Collection of simple types (String, Integer, etc.) → Single ARRAY characteristic
+ log.debug("Handling collection of {} simple types for field: {}", collection.size(), fieldName);
+ addArrayCharacteristic(spec, fieldName, convertCollectionToString(collection));
+ }
+ }
+
+ /**
+ * Get the entity type name from a collection field using reflection on generic type
+ *
+ * @param field The field to examine
+ * @return The entity type name (e.g., "EdgeSite", "Datacenter") if it's a collection of entities,
+ * or null if it's a collection of simple types
+ */
+ private String getCollectionEntityType(Field field) {
+ try {
+ // Get the generic type of the field
+ java.lang.reflect.Type genericType = field.getGenericType();
+
+ if (genericType instanceof java.lang.reflect.ParameterizedType) {
+ java.lang.reflect.ParameterizedType paramType = (java.lang.reflect.ParameterizedType) genericType;
+ java.lang.reflect.Type[] typeArgs = paramType.getActualTypeArguments();
+
+ if (typeArgs.length > 0 && typeArgs[0] instanceof Class) {
+ Class> elementType = (Class>) typeArgs[0];
+
+ if (isEntityType(elementType)) {
+ String entityTypeName = elementType.getSimpleName();
+ log.debug("Field {} has entity generic type: {}", field.getName(), entityTypeName);
+ return entityTypeName;
+ } else {
+ log.debug("Field {} has simple type generic type: {}",
+ field.getName(), elementType.getSimpleName());
+ return null;
+ }
+ }
+ }
+ } catch (Exception e) {
+ log.warn("Could not determine generic type for field {}: {}", field.getName(), e.getMessage());
+ }
+
+ // Default to null (treat as simple type collection)
+ return null;
+ }
+
+ /**
+ * Add an ARRAY characteristic for collections of simple types
+ */
+ private void addArrayCharacteristic(LogicalResourceSpecification spec, String fieldName, String arrayValue) {
+ ResourceSpecificationCharacteristic characteristic = new ResourceSpecificationCharacteristic();
+ characteristic.setName(fieldName);
+ characteristic.setValueType("ARRAY");
+ characteristic.setConfigurable(false);
+
+ // Create characteristic value
+ ResourceSpecificationCharacteristicValue charValue = new ResourceSpecificationCharacteristicValue();
+ charValue.setIsDefault(true);
+ charValue.setValueType("ARRAY");
+
+ // Set the array value
+ Any anyValue = new Any();
+ anyValue.setValue(arrayValue);
+ anyValue.setAlias(fieldName);
+ charValue.setValue(anyValue);
+
+ characteristic.addResourceSpecCharacteristicValueItem(charValue);
+ spec.addResourceSpecCharacteristicItem(characteristic);
+
+ log.debug("Added ARRAY characteristic: {} = {}", fieldName, arrayValue);
+ }
+
+ /**
+ * Convert collection to string representation (JSON-like format)
+ */
+ private String convertCollectionToString(Collection> collection) {
+ if (collection == null || collection.isEmpty()) {
+ return "[]";
+ }
+
+ StringBuilder sb = new StringBuilder("[");
+ boolean first = true;
+ for (Object item : collection) {
+ if (!first) {
+ sb.append(", ");
+ }
+ if (item instanceof String) {
+ sb.append("\"").append(item).append("\"");
+ } else {
+ sb.append(item);
+ }
+ first = false;
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ /**
+ * Add a characteristic for a simple field
+ */
+ private void addCharacteristic(LogicalResourceSpecification spec, String fieldName, Object value, Class> type) {
+ ResourceSpecificationCharacteristic characteristic = new ResourceSpecificationCharacteristic();
+ characteristic.setName(fieldName);
+ characteristic.setValueType(getValueType(type));
+ characteristic.setConfigurable(false);
+
+ // Create characteristic value
+ ResourceSpecificationCharacteristicValue charValue = new ResourceSpecificationCharacteristicValue();
+ charValue.setIsDefault(true);
+ charValue.setValueType(getValueType(type));
+
+ // Set the value
+ Any anyValue = new Any();
+ anyValue.setValue(convertValueToString(value));
+ anyValue.setAlias(fieldName);
+ charValue.setValue(anyValue);
+
+ characteristic.addResourceSpecCharacteristicValueItem(charValue);
+ spec.addResourceSpecCharacteristicItem(characteristic);
+
+ log.debug("Added characteristic: {} = {}", fieldName, value);
+ }
+
+ /**
+ * Add characteristics for value object fields
+ */
+ private void addValueObjectCharacteristics(LogicalResourceSpecification spec, String fieldName, Object valueObject) {
+ Class> voClass = valueObject.getClass();
+
+ for (Field voField : voClass.getDeclaredFields()) {
+ try {
+ voField.setAccessible(true);
+ Object voValue = voField.get(valueObject);
+
+ if (voValue != null && !java.lang.reflect.Modifier.isStatic(voField.getModifiers())) {
+ // Use nested naming: e.g., "coordinates.lat", "coordinates.lng"
+ String nestedFieldName = fieldName + "." + voField.getName();
+ addCharacteristic(spec, nestedFieldName, voValue, voField.getType());
+ }
+ } catch (IllegalAccessException e) {
+ log.warn("Could not access field {} in value object {}", voField.getName(), voClass.getSimpleName(), e);
+ }
+ }
+ }
+
+
+
+ /**
+ * Add a relationship for an entity field
+ */
+ private void addRelationship(LogicalResourceSpecification spec, String fieldName, Class> relatedClass, Object relatedEntity) {
+ ResourceSpecificationRelationship relationship = new ResourceSpecificationRelationship();
+
+ // Get the ID of the related entity
+ String relatedId = extractEntityId(relatedEntity);
+ relationship.setId(relatedId);
+ relationship.setName(relatedClass.getSimpleName());
+ relationship.setRelationshipType(RELATIONSHIP_TYPE_DEPENDENCY);
+ relationship.setRole(fieldName);
+
+ // Set href if we have an ID
+ if (relatedId != null) {
+ relationship.setHref("/api/topology/" + relatedClass.getSimpleName().toLowerCase() + "s/" + relatedId);
+ }
+
+ spec.addResourceSpecRelationshipItem(relationship);
+
+ log.debug("Added relationship: {} -> {} (id: {})", fieldName, relatedClass.getSimpleName(), relatedId);
+ }
+
+ /**
+ * Extract ID from an entity using reflection
+ */
+ private String extractEntityId(Object entity) {
+ try {
+ Field idField = findIdField(entity.getClass());
+ if (idField != null) {
+ idField.setAccessible(true);
+ Object id = idField.get(entity);
+ return id != null ? id.toString() : null;
+ }
+ } catch (IllegalAccessException e) {
+ log.warn("Could not extract ID from entity {}", entity.getClass().getSimpleName(), e);
+ }
+ return null;
+ }
+
+ /**
+ * Find the ID field in a class hierarchy
+ */
+ private Field findIdField(Class> clazz) {
+ while (clazz != null && !clazz.equals(Object.class)) {
+ for (Field field : clazz.getDeclaredFields()) {
+ if ("id".equals(field.getName())) {
+ return field;
+ }
+ }
+ clazz = clazz.getSuperclass();
+ }
+ return null;
+ }
+
+
+ /**
+ * Get TMF value type from Java type
+ */
+ private String getValueType(Class> type) {
+ if (type.equals(String.class)) {
+ return "TEXT";
+ } else if (type.equals(Integer.class) || type.equals(int.class) ||
+ type.equals(Long.class) || type.equals(long.class)) {
+ return "NUMBER";
+ } else if (type.equals(Boolean.class) || type.equals(boolean.class)) {
+ return "BOOLEAN";
+ } else if (type.equals(Double.class) || type.equals(double.class) ||
+ type.equals(Float.class) || type.equals(float.class)) {
+ return "NUMBER";
+ } else if (type.isEnum()) {
+ return "TEXT";
+ } else if (LocalDateTime.class.isAssignableFrom(type)) {
+ return "DATETIME";
+ } else if (Collection.class.isAssignableFrom(type)) {
+ return "ARRAY";
+ } else {
+ return "TEXT";
+ }
+ }
+
+ /**
+ * Convert value to string representation
+ */
+ private String convertValueToString(Object value) {
+ if (value == null) {
+ return "";
+ }
+
+ if (value instanceof Collection) {
+ // Convert collection to JSON-like string
+ Collection> collection = (Collection>) value;
+ return collection.toString();
+ } else if (value instanceof LocalDateTime) {
+ return value.toString();
+ } else if (value.getClass().isEnum()) {
+ return ((Enum>) value).name();
+ } else {
+ return value.toString();
+ }
+ }
+
+ // ========== Helper Methods for Common Interface Methods ==========
+
+ /**
+ * Get entity type name from either interface
+ */
+ private String getEntityTypeName(Object entity) {
+ if (entity instanceof LogicalResourceMappable) {
+ return ((LogicalResourceMappable) entity).getEntityTypeName();
+ } else if (entity instanceof LogicalResourceSpecMappable) {
+ return ((LogicalResourceSpecMappable) entity).getEntityTypeName();
+ }
+ return entity.getClass().getSimpleName();
+ }
+
+ /**
+ * Get entity ID from either interface
+ */
+ private String getEntityId(Object entity) {
+ if (entity instanceof LogicalResourceMappable) {
+ return ((LogicalResourceMappable) entity).getEntityId();
+ } else if (entity instanceof LogicalResourceSpecMappable) {
+ return ((LogicalResourceSpecMappable) entity).getEntityId();
+ }
+ return null;
+ }
+
+ /**
+ * Get entity description from either interface
+ */
+ private String getEntityDescription(Object entity) {
+ if (entity instanceof LogicalResourceMappable) {
+ return ((LogicalResourceMappable) entity).getEntityDescription();
+ } else if (entity instanceof LogicalResourceSpecMappable) {
+ return ((LogicalResourceSpecMappable) entity).getEntityDescription();
+ }
+ return "";
+ }
+
+ /**
+ * Get version from either interface
+ */
+ private String getVersion(Object entity) {
+ if (entity instanceof LogicalResourceMappable) {
+ return ((LogicalResourceMappable) entity).getVersion();
+ } else if (entity instanceof LogicalResourceSpecMappable) {
+ return ((LogicalResourceSpecMappable) entity).getVersion();
+ }
+ return "0.0.1";
+ }
+
+ /**
+ * Get entity name from either interface
+ * Returns the actual name field value from the entity
+ */
+ private String getEntityName(Object entity) {
+ if (entity instanceof LogicalResourceMappable) {
+ return ((LogicalResourceMappable) entity).getEntityName();
+ } else if (entity instanceof LogicalResourceSpecMappable) {
+ return ((LogicalResourceSpecMappable) entity).getEntityName();
+ }
+ return "";
+ }
+
+}
+
diff --git a/src/main/java/org/etsi/osl/controllers/ietf/ns/mappers/LogicalResourceToEntityMapper.java b/src/main/java/org/etsi/osl/controllers/ietf/ns/mappers/LogicalResourceToEntityMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..007f0883252e3692e9976abe16f17c0789174de7
--- /dev/null
+++ b/src/main/java/org/etsi/osl/controllers/ietf/ns/mappers/LogicalResourceToEntityMapper.java
@@ -0,0 +1,521 @@
+package org.etsi.osl.controllers.ietf.ns.mappers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.etsi.osl.controllers.ietf.ns.api.CategoryConfigurationService;
+import org.etsi.osl.controllers.ietf.ns.api.domain.model.SloSleTemplate;
+import org.etsi.osl.controllers.ietf.ns.domain.common.LogicalResourceMappable;
+import org.etsi.osl.controllers.ietf.ns.domain.common.RelatedManagedResourceReference;
+import org.etsi.osl.controllers.ietf.ns.repository.TMFResourceInventoryRepository;
+import org.etsi.osl.tmf.common.model.Any;
+import org.etsi.osl.tmf.ri639.model.Characteristic;
+import org.etsi.osl.tmf.ri639.model.LogicalResource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Mapper utility to transform TMF639 LogicalResource instances back to domain entities.
+ *
+ * Strategy:
+ * - Use registry pattern for entity type dispatch (replaces switch statement)
+ * - Generic method using LogicalResourceMappable interface for field mapping
+ * - Extract characteristics and map to entity fields via field mappings
+ * - Handle value objects (Coordinates, Capacity, Contact)
+ * - Map TMF resource states back to SiteStatus/RegionStatus
+ * - Use category from CategoryConfigurationService for consistent category format
+ *
+ * Changes from legacy version:
+ * - Removed 11 individual converter methods (toDatacenter, toEdgeSite, etc.)
+ * - Replaced with single generic fromLogicalResource(LogicalResource, Class) method
+ * - Uses entity's getFieldMappings() interface method for field-to-characteristic mappings
+ * - Registry pattern replaces 18-case switch statement
+ * - Code reduction: ~791 lines → ~520 lines (34% reduction)
+ */
+@Component
+@Slf4j
+public class LogicalResourceToEntityMapper {
+
+ @Autowired
+ private CategoryConfigurationService categoryConfig;
+
+ @Autowired(required = false)
+ private TMFResourceInventoryRepository tmfInventoryRepository;
+
+ // Registry mapping entity type names to their classes
+ private static final Map> ENTITY_REGISTRY =
+ Map.ofEntries(
+ //Map.entry("SloSleTemplate", SloSleTemplate.class)
+ );
+
+ /**
+ * Convert LogicalResource to appropriate entity type based on category.
+ * Uses registry to dispatch to correct entity class.
+ *
+ * @param resource LogicalResource to convert
+ * @return Entity instance of the appropriate type (Site, Region, or PhysicalDevice)
+ * @throws IllegalArgumentException if category is null or unknown entity type
+ */
+ public Object fromLogicalResource(LogicalResource resource) {
+ String category = resource.getCategory();
+ if (category == null) {
+ throw new IllegalArgumentException("LogicalResource category is null");
+ }
+
+ String entityType = extractEntityType(category);
+ log.debug("Mapping LogicalResource to {} entity", entityType);
+
+ Class extends LogicalResourceMappable> entityClass = ENTITY_REGISTRY.get(entityType);
+ if (entityClass == null) {
+ throw new IllegalArgumentException("Unknown entity type: " + entityType);
+ }
+
+ return fromLogicalResource(resource, entityClass);
+ }
+
+ /**
+ * Generic conversion method for any LogicalResourceMappable entity.
+ * Replaces 11 individual converter methods (toDatacenter, toEdgeSite, toComputeDevice, etc.)
+ *
+ * @param Entity type extending LogicalResourceMappable
+ * @param resource LogicalResource to convert
+ * @param entityClass Target entity class to instantiate
+ * @return Entity instance with all fields mapped from characteristics
+ */
+ public T fromLogicalResource(LogicalResource resource, Class entityClass) {
+ try {
+ T entity = entityClass.getDeclaredConstructor().newInstance();
+ log.debug("Mapping LogicalResource to {}", entityClass.getSimpleName());
+
+ // Get characteristics map for field lookups
+ Map charMap = getCharacteristicsMap(resource);
+
+ // Let entity populate its own basic fields from LogicalResource
+ entity.mapFromLogicalResource(resource, charMap);
+
+ // Apply field mappings from entity's getFieldMappings()
+ applyFieldMappings(entity, charMap);
+
+ // Handle relationships if entity declares them
+ if (entity.hasRelationships()) {
+ applyRelationships(entity, resource, charMap);
+ }
+ // Handle related managed resources for any entity implementing RelatedManagedResource
+ if (entity instanceof RelatedManagedResourceReference relatedResource) {
+ // Extract relatedManagedResourceId from ResourceRelationship
+ String relatedResourceId = TransformationUtils.extractRelationshipId(resource, "relatedManagedResource");
+ if (relatedResourceId != null) {
+ relatedResource.setRelatedManagedResourceId(relatedResourceId);
+ }
+ // Fetch and populate relatedManagedResource from inventory
+ setRelatedManagedResource(entity, relatedResource);
+ }
+
+ log.debug("Successfully mapped {} to {}", resource.getUuid(), entityClass.getSimpleName());
+ return entity;
+
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to convert LogicalResource to " + entityClass.getSimpleName() + ": " + e.getMessage(), e);
+ }
+ }
+
+
+
+
+ /**
+ * Apply field mappings declared in entity's getFieldMappings() method
+ */
+ private void applyFieldMappings(T entity, Map charMap) {
+ if (!charMap.isEmpty()) {
+ for (Map.Entry fieldMapping : entity.getFieldMappings().entrySet()) {
+ String charName = fieldMapping.getKey();
+ String fieldName = fieldMapping.getValue();
+ String charValue = charMap.get(charName);
+
+ if (charValue != null && !charValue.isEmpty()) {
+ try {
+ applyFieldValue(entity, fieldName, charValue);
+ } catch (Exception e) {
+ log.warn("Could not apply field mapping {} = {}: {}", fieldName, charValue, e.getMessage());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Apply a characteristic value to an entity field using reflection
+ */
+ @SuppressWarnings("unchecked")
+ private void applyFieldValue(T entity, String fieldName, String charValue) {
+ try {
+ // Use helper method to find field in class hierarchy
+ java.lang.reflect.Field field = findFieldInHierarchy(entity.getClass(), fieldName);
+ if (field == null) {
+ log.trace("Field {} not found in class hierarchy of {}", fieldName, entity.getClass().getSimpleName());
+ return;
+ }
+ field.setAccessible(true);
+ Class> fieldType = field.getType();
+
+ Object value = null;
+ if (fieldType == String.class) {
+ value = charValue;
+ } else if (fieldType == Integer.class) {
+ value = Integer.parseInt(charValue);
+ } else if (fieldType == Double.class) {
+ value = Double.parseDouble(charValue);
+ } else if (fieldType == Boolean.class) {
+ value = Boolean.parseBoolean(charValue);
+ } else if (fieldType == List.class) {
+ value = parseArrayCharacteristic(charValue);
+ } else if (fieldType.isEnum()) {
+ try {
+ value = Enum.valueOf((Class extends Enum>) fieldType, charValue);
+ } catch (IllegalArgumentException e) {
+ log.warn("Could not parse enum value {} for field {}", charValue, fieldName);
+ }
+ }
+
+ if (value != null) {
+ field.set(entity, value);
+ }
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ log.warn("Error setting field {} on {}: {}", fieldName, entity.getClass().getSimpleName(), e.getMessage());
+ }
+ }
+
+ /**
+ * Apply relationships to entity based on getRelationshipFields()
+ */
+ private void applyRelationships(T entity, LogicalResource resource, Map charMap) {
+ for (String relationshipField : entity.getRelationshipFields()) {
+ try {
+ applyRelationshipField(entity, relationshipField, resource);
+ } catch (Exception e) {
+ log.warn("Could not apply relationship field {}: {}", relationshipField, e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Apply a single relationship field value
+ */
+ private void applyRelationshipField(T entity, String relationshipField, LogicalResource resource) {
+ try {
+ // Use helper method to find field in class hierarchy
+ java.lang.reflect.Field field = findFieldInHierarchy(entity.getClass(), relationshipField);
+ if (field == null) {
+ log.trace("Relationship field {} not found in class hierarchy of {}", relationshipField, entity.getClass().getSimpleName());
+ return;
+ }
+ field.setAccessible(true);
+
+ // Check if this is a collection relationship (List) or single entity relationship
+ if (java.util.Collection.class.isAssignableFrom(field.getType())) {
+ // Handle collection relationships (e.g., physicalDevices)
+ List