diff --git a/src/api/main.py b/src/api/main.py index 365561e5a6589b8a08b5acaf199f6db5e3ae7005..61248537264e54d118d070e97be138a4e0105e55 100644 --- a/src/api/main.py +++ b/src/api/main.py @@ -844,7 +844,10 @@ class Api: xpath = f"/ietf-network-slice-service:network-slice-services" if not current_app.config["DUMMY_MODE"]: content = get_data_store(xpath) - for slice in content["network-slice-services"]["slice-service"]: + slice_services = safe_get(content, ["network-slice-services", "slice-service"]) + if not slice_services: + raise ValueError("Network slice services not found") + for slice in slice_services: slice_type = list(slice["service-tags"]["tag-type"]["ietf-network-slice-service:service"]["tag-type-value"])[0] if not slice_type: slice_type = "L2" @@ -923,7 +926,10 @@ class Api: xpath = f"/ietf-network-slice-service:network-slice-services/slice-service" if not current_app.config["DUMMY_MODE"]: content = get_data_store(xpath) - for slice in content["network-slice-services"]["slice-service"]: + slice_services = safe_get(content, ["network-slice-services", "slice-service"]) + if not slice_services: + raise ValueError("Slice services not found") + for slice in slice_services: slice_type = list(slice["service-tags"]["tag-type"]["ietf-network-slice-service:service"]["tag-type-value"])[0] if not slice_type: slice_type = "L2" diff --git a/src/main.py b/src/main.py index 550ef6efd7366310f616f5629536fa12ebb2bc8b..f59dedf1c8327fc7942fde7e80c29e581a106cd7 100644 --- a/src/main.py +++ b/src/main.py @@ -20,6 +20,7 @@ from src.utils.dump_templates import dump_templates from src.utils.build_response import build_response from src.nbi_processor.main import nbi_processor from src.database.store_data import store_data +from src.database.service_db import delete_data from src.mapper.main import mapper from src.realizer.main import realizer from src.planner.shortest_path import get_shortest_path @@ -126,7 +127,14 @@ class NSController: # Send config to controllers is_update = True if slice_id else False - response = send_controller(self.controller_type, requests, is_update=is_update, old_service_id=old_service_id) + try: + response = send_controller(self.controller_type, requests, is_update=is_update, old_service_id=old_service_id) + except Exception as e: + for service in services: + service_id = service.get("id") + if service_id: + delete_data(service_id) + raise Exception(f"Controller upload failed: {e}") if not response: raise Exception("Controller upload failed") 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 af7c8e944db9920b51a85d54611728448b596ff6..648218f99e867c1ab0c4be0df9a36e118e82d53d 100644 --- a/src/realizer/restconf/service_types/builders/configure_match_criteria.py +++ b/src/realizer/restconf/service_types/builders/configure_match_criteria.py @@ -66,6 +66,8 @@ def configure_match_criteria(network_access, site, sdp, layer_type): site["routing-protocols"]["routing-protocol"].append(routing_protocol) return + elif layer_type == "l2" and match_type == "vlan": + network_access["connection"]["tagged-interface"]["dot1q-vlan-tagged"]["cvlan-id"] = value # Do not add rule when match type is any if match_type == "any": 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 0876f50fea1e70480f8ca7e2a40b814a96f69ec0..c52cb598572a9865c2d64ba45050d55320c449ec 100644 --- a/src/realizer/restconf/service_types/builders/create_network_access.py +++ b/src/realizer/restconf/service_types/builders/create_network_access.py @@ -15,8 +15,6 @@ # This file is an original contribution from Telefonica Innovación Digital S.L. from src.utils.safe_get import safe_get -from .configure_match_criteria import configure_match_criteria -from .configure_slos import configure_slos def create_network_access(sdp, ietf_intent, connectivity_type, router_id, router_if, layer_type): """Crea la configuración de acceso a la red del site.""" @@ -38,22 +36,15 @@ def create_network_access(sdp, ietf_intent, connectivity_type, router_id, router } } } - - if sdp['type'] == "sender": - site_role = "hub-role" - elif sdp['type'] == "receiver": - site_role = "spoke-role" else: - site_role = "any-to-any-role" + raise ValueError(f"Unsupported layer_type: {layer_type}") network_access = { access_id: router_if, - # access_type: f"{connectivity_type}", - access_type: "multipoint", # We set to multipoint to avoid errors in Teraflow + access_type: connectivity_type, "device-reference": router_id, "vpn-attachment": { - "vpn-id": ietf_intent["id"], - "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 + "vpn-id": ietf_intent["id"] }, "service": { "qos": { @@ -69,14 +60,43 @@ def create_network_access(sdp, ietf_intent, connectivity_type, router_id, router } } } + + site_type = safe_get(sdp, ["type"]) + if connectivity_type == "ietf-vpn-common:hub-spoke": + if site_type == "sender": + site_role = "hub-role" + elif site_type == "receiver": + site_role = "spoke-role" + else: + raise ValueError(f"Unsupported site_type for hub-spoke connectivity: {site_type}") + elif connectivity_type == "ietf-vpn-common:any-to-any": + site_role = "any-to-any-role" + else: + site_role = None # For point-to-point or other types, we do not set a site role + + if site_role: + network_access["vpn-attachment"]["site-role"] = site_role + if layer_type == "l3": network_access["ip-connection"] = ip_connection elif layer_type == "l2": # This should not be needed, but we add it because TFS requires it network_access["connection"] = { + "encapsulation-type": "vlan", + "eth-inf-type": "ietf-l2vpn-svc:tagged", + "tagged-interface": { + "type": "ietf-l2vpn-svc:dot1q", + "dot1q-vlan-tagged": { + "cvlan-id": 1 + } + }, "oam": { "md-name": "test", "md-level": 0 } } + sdp_id = safe_get(sdp, ["sdp", "id"]) or safe_get(sdp, ["sdp", "node-id"]) + network_access["bearer"] = { + "bearer-reference": sdp_id + } 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 6ff72e93f9496ea3f30b076d1db24e44aea49663..536cd9b636233e788d8b2c366b0dd0d9f5d6f6e6 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 @@ -64,6 +64,9 @@ def create_site_from_sdp(sdp, ietf_intent, connectivity_type, layer_type): } } + if layer_type == "l2": + site["default-ce-vlan-id"] = 1 + if layer_type == "l3": site["routing-protocols"] = {"routing-protocol": []} diff --git a/src/realizer/restconf/service_types/builders/initialize_structure.py b/src/realizer/restconf/service_types/builders/initialize_structure.py index 05c1267338a960c75730771c4793c3361ce07c52..d8d652f09bf427fc2acbad0b2d368fe08dc9a6ac 100644 --- a/src/realizer/restconf/service_types/builders/initialize_structure.py +++ b/src/realizer/restconf/service_types/builders/initialize_structure.py @@ -32,6 +32,7 @@ def initialize_structure(vpn_id, connectivity_type, layer_type): structure[f"ietf-{layer_type}vpn-svc:{layer_type}vpn-svc"]["vpn-services"]["vpn-service"][0] = { "vpn-id": vpn_id, "ce-vlan-preservation": False, - "ce-vlan-cos-preservation": False + "ce-vlan-cos-preservation": False, + "frame-delivery": { "multicast-gp-port-mapping":"static-mapping"} # This field should not be needed, it is optional but the YANG validator in TFS sets it to mandatory } return structure \ No newline at end of file