From 5bc812456c6e5ed350e03070f0153a5d254e1647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Vel=C3=A1zquez?= Date: Wed, 6 May 2026 15:58:06 +0000 Subject: [PATCH 1/4] L3VPN Add routing protocols and vlan match Network access: change custommer address, site role and access type L2VPN Change initial structure --- .../builders/apply_metric_constraint.py | 4 +- .../builders/configure_match_criteria.py | 40 +++++++++++++++---- .../builders/create_network_access.py | 25 ++++++++---- .../builders/create_site_from_sdp.py | 13 +++++- .../builders/initialize_structure.py | 7 ++-- 5 files changed, 67 insertions(+), 22 deletions(-) diff --git a/src/realizer/restconf/service_types/builders/apply_metric_constraint.py b/src/realizer/restconf/service_types/builders/apply_metric_constraint.py index 06f799e..f98ec94 100644 --- a/src/realizer/restconf/service_types/builders/apply_metric_constraint.py +++ b/src/realizer/restconf/service_types/builders/apply_metric_constraint.py @@ -31,14 +31,14 @@ def apply_metric_constraint(service, qos_class, constraint, vpn_id, layer_type): "direction": "input-bw", "vpn-id": vpn_id, "cir": bandwidth, - "cbs": bandwidth*0.05 + "cbs": int(bandwidth*0.05) }, { "type": "bw-per-svc", "direction": "output-bw", "vpn-id": vpn_id, "cir": bandwidth, - "cbs": bandwidth*0.05 + "cbs": int(bandwidth*0.05) }, ] } diff --git a/src/realizer/restconf/service_types/builders/configure_match_criteria.py b/src/realizer/restconf/service_types/builders/configure_match_criteria.py index ffff478..5c0788e 100644 --- a/src/realizer/restconf/service_types/builders/configure_match_criteria.py +++ b/src/realizer/restconf/service_types/builders/configure_match_criteria.py @@ -18,7 +18,7 @@ import logging from src.utils.safe_get import safe_get -def configure_match_criteria(network_access, sdp, layer_type): +def configure_match_criteria(network_access, site, sdp, layer_type): """Configura los criterios de coincidencia en el acceso a la red.""" MATCH_TYPE_MAPPING = { "dscp": "dscp" @@ -26,6 +26,7 @@ def configure_match_criteria(network_access, sdp, layer_type): if layer_type == "l3": MATCH_TYPE_MAPPING["source-ip-prefix"] = "ipv4-src-prefix" MATCH_TYPE_MAPPING["destination-ip-prefix"] = "ipv4-dst-prefix" + MATCH_TYPE_MAPPING["vlan"] = "vlan" # Need to check if applies with l2 too match_criteria = sdp.get("match_criteria") if not match_criteria: @@ -41,11 +42,34 @@ def configure_match_criteria(network_access, sdp, layer_type): logging.warning(f"Unknown match type: {match_type}") return - rule = { - "id": f"match-{match_type}-{index}", - "match-flow": { - MATCH_TYPE_MAPPING[match_type]: value + provider_address = safe_get(sdp, ["sdp", "attachment-circuits", "attachment-circuit", 0, "ac-ipv4-address"]) + prefix_length = safe_get(sdp, ["sdp", "attachment-circuits", "attachment-circuit", 0, "ac-ipv4-prefix-length"]) + lan = f"{provider_address}/{prefix_length}" if provider_address and prefix_length else None + + if match_type != "vlan": + rule = { + "id": f"match-{match_type}-{index}", + "match-flow": { + MATCH_TYPE_MAPPING[match_type]: value + } } - } - - network_access["service"]["qos"]["qos-classification-policy"]["rule"].append(rule) \ No newline at end of file + + network_access["service"]["qos"]["qos-classification-policy"]["rule"].append(rule) + else: + # Handle VLAN match criteria + routing_protocol = { + "type": "static", + "static":{ + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes":[ + { + "lan": lan, + "lan-tag": value, + "next-hop": provider_address # This is not correct, should be the management ip of the provider router, but we don't have that info in the SDP. Need to check how to handle this. + } + ] + } + } + } + + site["routing-protocols"]["routing-protocol"].append(routing_protocol) diff --git a/src/realizer/restconf/service_types/builders/create_network_access.py b/src/realizer/restconf/service_types/builders/create_network_access.py index c4725b5..0876f50 100644 --- a/src/realizer/restconf/service_types/builders/create_network_access.py +++ b/src/realizer/restconf/service_types/builders/create_network_access.py @@ -33,19 +33,27 @@ def create_network_access(sdp, ietf_intent, connectivity_type, router_id, router "address-allocation-type": "static-address", "addresses": { "provider-address": safe_get(sdp, ["sdp", "attachment-circuits", "attachment-circuit", 0, "ac-ipv4-address"]), - # "customer-address": "", + "customer-address": safe_get(sdp, ["sdp", "attachment-circuits", "attachment-circuit", 0, "ac-ipv4-address"]), # We set the same address for provider and customer because we don't have the customer address in the SDP. This is not correct and should be fixed in the future when we have that info. "prefix-length": safe_get(sdp, ["sdp", "attachment-circuits", "attachment-circuit", 0, "ac-ipv4-prefix-length"]) } } } + if sdp['type'] == "sender": + site_role = "hub-role" + elif sdp['type'] == "receiver": + site_role = "spoke-role" + else: + site_role = "any-to-any-role" + network_access = { access_id: router_if, - access_type: f"{connectivity_type}", + # access_type: f"{connectivity_type}", + access_type: "multipoint", # We set to multipoint to avoid errors in Teraflow "device-reference": router_id, "vpn-attachment": { "vpn-id": ietf_intent["id"], - # "site-role": f"{sdp['type']}" # Revisar esto!!!! + "site-role": site_role # This will be changed in the future. Hub and spoke roles are only for multipoint connectivity constructs, there is no defined roles por point to point constructs }, "service": { "qos": { @@ -63,9 +71,12 @@ def create_network_access(sdp, ietf_intent, connectivity_type, router_id, router } if layer_type == "l3": network_access["ip-connection"] = ip_connection - - # Configurar match criteria y SLOs - configure_match_criteria(network_access, sdp, layer_type) - configure_slos(network_access, ietf_intent, layer_type) + elif layer_type == "l2": # This should not be needed, but we add it because TFS requires it + network_access["connection"] = { + "oam": { + "md-name": "test", + "md-level": 0 + } + } return network_access diff --git a/src/realizer/restconf/service_types/builders/create_site_from_sdp.py b/src/realizer/restconf/service_types/builders/create_site_from_sdp.py index 0053f0b..5f05f42 100644 --- a/src/realizer/restconf/service_types/builders/create_site_from_sdp.py +++ b/src/realizer/restconf/service_types/builders/create_site_from_sdp.py @@ -17,6 +17,8 @@ import logging from src.utils.safe_get import safe_get from .create_network_access import create_network_access +from .configure_match_criteria import configure_match_criteria +from .configure_slos import configure_slos def create_site_from_sdp(sdp, ietf_intent, connectivity_type, layer_type): """ @@ -39,6 +41,8 @@ def create_site_from_sdp(sdp, ietf_intent, connectivity_type, layer_type): logging.debug(f"Configured site for SDP {safe_get(sdp, ['sdp', 'id'])} with location: {location}, router_id: {router_id}, router_if: {router_if}") + network_access = create_network_access(sdp, ietf_intent, connectivity_type, router_id, router_if, layer_type) + # Crear estructura del site site = { "site-id": safe_get(sdp, ["sdp", "id"]), @@ -55,8 +59,15 @@ def create_site_from_sdp(sdp, ietf_intent, connectivity_type, layer_type): "type": "provider-managed" }, "site-network-accesses": { - "site-network-access": [create_network_access(sdp, ietf_intent, connectivity_type, router_id, router_if, layer_type)] + "site-network-access": [network_access] } } + + if layer_type == "l3": + site["routing-protocols"] = {"routing-protocol": []} + + # Configure match criteria and SLOs + configure_match_criteria(network_access, site, sdp, layer_type) + configure_slos(network_access, ietf_intent, layer_type) return site diff --git a/src/realizer/restconf/service_types/builders/initialize_structure.py b/src/realizer/restconf/service_types/builders/initialize_structure.py index 7d81f64..05c1267 100644 --- a/src/realizer/restconf/service_types/builders/initialize_structure.py +++ b/src/realizer/restconf/service_types/builders/initialize_structure.py @@ -29,10 +29,9 @@ def initialize_structure(vpn_id, connectivity_type, layer_type): } } if layer_type == "l2": - structure[f"ietf-{layer_type}vpn-svc:{layer_type}vpn-svc"]["vpn-services"]["vpn-service"] = { + structure[f"ietf-{layer_type}vpn-svc:{layer_type}vpn-svc"]["vpn-services"]["vpn-service"][0] = { "vpn-id": vpn_id, - "customer-name": "osm", - "vpn-svc-type": "vpws", # Lo dejamos en vpws porque es el unico que encaja - "svc-topo": connectivity_type, + "ce-vlan-preservation": False, + "ce-vlan-cos-preservation": False } return structure \ No newline at end of file -- GitLab From cfba738cb4b7504fde342773017de8ae4474c7d4 Mon Sep 17 00:00:00 2001 From: velazquez Date: Fri, 8 May 2026 12:02:34 +0000 Subject: [PATCH 2/4] Add match type "any" --- .../service_types/builders/configure_match_criteria.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/realizer/restconf/service_types/builders/configure_match_criteria.py b/src/realizer/restconf/service_types/builders/configure_match_criteria.py index 5c0788e..6cd72c9 100644 --- a/src/realizer/restconf/service_types/builders/configure_match_criteria.py +++ b/src/realizer/restconf/service_types/builders/configure_match_criteria.py @@ -27,6 +27,7 @@ def configure_match_criteria(network_access, site, sdp, layer_type): MATCH_TYPE_MAPPING["source-ip-prefix"] = "ipv4-src-prefix" MATCH_TYPE_MAPPING["destination-ip-prefix"] = "ipv4-dst-prefix" MATCH_TYPE_MAPPING["vlan"] = "vlan" # Need to check if applies with l2 too + MATCH_TYPE_MAPPING["any"] = "any" # Need to check if applies with l2 too match_criteria = sdp.get("match_criteria") if not match_criteria: @@ -46,7 +47,7 @@ def configure_match_criteria(network_access, site, sdp, layer_type): prefix_length = safe_get(sdp, ["sdp", "attachment-circuits", "attachment-circuit", 0, "ac-ipv4-prefix-length"]) lan = f"{provider_address}/{prefix_length}" if provider_address and prefix_length else None - if match_type != "vlan": + if match_type != "vlan" and match_type != "any": rule = { "id": f"match-{match_type}-{index}", "match-flow": { @@ -55,7 +56,8 @@ def configure_match_criteria(network_access, site, sdp, layer_type): } network_access["service"]["qos"]["qos-classification-policy"]["rule"].append(rule) - else: + elif match_type == "vlan": + site["routing-protocols"] = {"routing-protocol": []} # Handle VLAN match criteria routing_protocol = { "type": "static", -- GitLab From d5f3d177c109f372ceb9dd6de4d48b5293a4b58f Mon Sep 17 00:00:00 2001 From: velazquez Date: Fri, 8 May 2026 12:57:17 +0000 Subject: [PATCH 3/4] Add vlan match to l2 services --- .../builders/configure_match_criteria.py | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/realizer/restconf/service_types/builders/configure_match_criteria.py b/src/realizer/restconf/service_types/builders/configure_match_criteria.py index 6cd72c9..af7c8e9 100644 --- a/src/realizer/restconf/service_types/builders/configure_match_criteria.py +++ b/src/realizer/restconf/service_types/builders/configure_match_criteria.py @@ -21,13 +21,13 @@ from src.utils.safe_get import safe_get def configure_match_criteria(network_access, site, sdp, layer_type): """Configura los criterios de coincidencia en el acceso a la red.""" MATCH_TYPE_MAPPING = { - "dscp": "dscp" + "dscp": "dscp", + "vlan": "dot1q", + "any": "any" } if layer_type == "l3": MATCH_TYPE_MAPPING["source-ip-prefix"] = "ipv4-src-prefix" MATCH_TYPE_MAPPING["destination-ip-prefix"] = "ipv4-dst-prefix" - MATCH_TYPE_MAPPING["vlan"] = "vlan" # Need to check if applies with l2 too - MATCH_TYPE_MAPPING["any"] = "any" # Need to check if applies with l2 too match_criteria = sdp.get("match_criteria") if not match_criteria: @@ -47,23 +47,13 @@ def configure_match_criteria(network_access, site, sdp, layer_type): prefix_length = safe_get(sdp, ["sdp", "attachment-circuits", "attachment-circuit", 0, "ac-ipv4-prefix-length"]) lan = f"{provider_address}/{prefix_length}" if provider_address and prefix_length else None - if match_type != "vlan" and match_type != "any": - rule = { - "id": f"match-{match_type}-{index}", - "match-flow": { - MATCH_TYPE_MAPPING[match_type]: value - } - } - - network_access["service"]["qos"]["qos-classification-policy"]["rule"].append(rule) - elif match_type == "vlan": + if layer_type == "l3" and match_type == "vlan": site["routing-protocols"] = {"routing-protocol": []} - # Handle VLAN match criteria routing_protocol = { "type": "static", - "static":{ + "static": { "cascaded-lan-prefixes": { - "ipv4-lan-prefixes":[ + "ipv4-lan-prefixes": [ { "lan": lan, "lan-tag": value, @@ -75,3 +65,17 @@ def configure_match_criteria(network_access, site, sdp, layer_type): } site["routing-protocols"]["routing-protocol"].append(routing_protocol) + return + + # Do not add rule when match type is any + if match_type == "any": + return + + rule = { + "id": f"match-{match_type}-{index}", + "match-flow": { + MATCH_TYPE_MAPPING[match_type]: value + } + } + + network_access["service"]["qos"]["qos-classification-policy"]["rule"].append(rule) -- GitLab From 4037a3ca72ebf9af81eecd58845b606f29891311 Mon Sep 17 00:00:00 2001 From: velazquez Date: Fri, 8 May 2026 13:55:35 +0000 Subject: [PATCH 4/4] Fix bug --- src/api/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/main.py b/src/api/main.py index a4a5957..221a45f 100644 --- a/src/api/main.py +++ b/src/api/main.py @@ -618,7 +618,7 @@ class Api: slice_type = "L2" logging.warning(f"Slice type not found in slice intent. Defaulting to L2") logging.debug(f"Send slice to delete in TFS with slice_type {slice_type}") - tfs_connector().nbi_delete(current_app.config["TFS_IP"], slice_type, slice.get("id")) + tfs_connector().nbi_delete(current_app.config["RESTCONF_IP"], slice_type, slice.get("id")) if current_app.config["TFS_L2VPN_SUPPORT"]: self.slice_service.tfs_l2vpn_delete() @@ -670,7 +670,7 @@ class Api: slice_type = "L2" logging.warning(f"Slice type not found in slice intent. Defaulting to L2") logging.debug(f"Send slice to delete in TFS with slice_type {slice_type}") - tfs_connector().nbi_delete(current_app.config["TFS_IP"], slice_type, existing_slice.get("id")) + tfs_connector().nbi_delete(current_app.config["RESTCONF_IP"], slice_type, existing_slice.get("id")) if current_app.config["TFS_L2VPN_SUPPORT"]: self.slice_service.tfs_l2vpn_delete() @@ -689,7 +689,7 @@ class Api: slice_type = "L2" logging.warning(f"Slice type not found in slice intent. Defaulting to L2") logging.debug(f"Send slice to delete in TFS with slice_type {slice_type}") - tfs_connector().nbi_delete(current_app.config["TFS_IP"], slice_type, slice.get("id")) + tfs_connector().nbi_delete(current_app.config["RESTCONF_IP"], slice_type, slice.get("id")) if current_app.config["TFS_L2VPN_SUPPORT"]: self.slice_service.tfs_l2vpn_delete() delete_data_store(xpath) -- GitLab