Commit 36f8d72d authored by Javier Velázquez's avatar Javier Velázquez
Browse files

Add PUT operation

parent 454eb176
Loading
Loading
Loading
Loading
+277 −19
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ from flask import current_app
from src.database.db import get_data, delete_data, get_all_data, delete_all_data
from src.realizer.tfs.helpers.tfs_connector import tfs_connector
from src.utils.safe_get import safe_get
from src.database.sysrepo_store import get_data_store, create_data_store, delete_data_store
from src.database.sysrepo_store import get_data_store, create_data_store, delete_data_store, update_data_store, normalize_libyang_data

class Api:
    def __init__(self, slice_service):
@@ -299,15 +299,14 @@ class Api:

    def add_network_slice_service(self, intent):
        try:
            # result = self.slice_service.nsc(intent)
            result = self.slice_service.nsc(intent)
            if result:
                create_data_store(intent)
            # if not result:
            #     return send_response(False, code=404, message="No intents found")
            logging.info(f"Slice created successfully")
                logging.info(f"Network Slice created successfully")
            return send_response(
                True,
                code=201,
                data="Data stored successfully" 
                data=result 
            )
        except RuntimeError as e:
            # Handle case where there is no content to process
@@ -328,7 +327,7 @@ class Api:
            return send_response(
                True,
                code=201,
                message="Template created successfully"
                data="Template created successfully" # Añadir otra data?
            )
        except Exception as e:
            # Handle unexpected errors
@@ -336,21 +335,47 @@ class Api:
    
    def add_slice_service(self, intent):
        try:
            slice_id = intent.pop("id", None)
            xpath = f"/ietf-network-slice-service:network-slice-services/slice-service[id='{slice_id}']"

            xpath = f"/ietf-network-slice-service:network-slice-services/slice-service[id='{intent.get('id')}']"
            existing_slice = get_data_store(xpath)
            if existing_slice:
                return send_response(False, code=409, message="Slice already exists")
            
            if "slo-sle-template" in intent:
                template_ref = intent.get("slo-sle-template")
                xpath_template = f"/ietf-network-slice-service:network-slice-services/slo-sle-templates/slo-sle-template[id='{template_ref}']"
                existing_template = get_data_store(xpath_template)
                if not existing_template:
                    return send_response(False, code=404, message="Referenced SLO/SLE template not found")
                full_intent = {
                    "ietf-network-slice-service:network-slice-services": {
                        "slo-sle-templates": {
                            "slo-sle-template": [existing_template["network-slice-services"]["slo-sle-templates"]["slo-sle-template"][template_ref]]
                        },
                        "slice-service": [intent]
                    }
                }
            elif "service-slo-sle-policy" in intent:
                full_intent = {
                    "ietf-network-slice-service:network-slice-services": {
                        "slo-sle-templates": {
                            "slo-sle-template": intent.get("service-slo-sle-policy")
                        },
                        "slice-service": [intent]
                    }
                }
            else:
                return send_response(False, code=400, message="No SLO/SLE template or policy provided in intent")
            
            full_intent = normalize_libyang_data(full_intent)
            result = self.slice_service.nsc(full_intent)
            if result:
                intent.pop("id", None)
                create_data_store(intent, xpath)
            #result = self.slice_service.nsc(intent)
            # if not result:
            #     return send_response(False, code=404, message="No intents found")
                logging.info(f"Slice created successfully")
            return send_response(
                True,
                code=201,
                data="Data stored successfully"
                data=result 
            )
        except RuntimeError as e:
            # Handle case where there is no content to process
@@ -377,14 +402,226 @@ class Api:
            # Handle unexpected errors
            return send_response(False, code=500, message=str(e))

    ### PUT

    def update_network_slice_service(self, intent):
        """
        Modifica (reemplaza) toda la configuración de network-slice-services
        """
        try:
            xpath = "/ietf-network-slice-service:network-slice-services"
            
            # Verificar que existe algo que modificar
            existing_data = get_data_store(xpath)  
            if not existing_data:
                return send_response(False, code=404, message="Network slice services not found")
            
            # Si no está en modo DUMMY, procesar con TFS
            result = self.slice_service.nsc(intent)
            if not result:
                return send_response(False, code=500, message="Failed to process slice in TFS")
            
            # Reemplazar completamente el recurso
            update_data_store(intent)
            logging.info("Network slice services modified successfully")
            
            return send_response(
                True,
                code=200,
                message="Network slice services updated successfully"
            )
        
        except ValueError as e:
            return send_response(False, code=404, message=str(e))
        except Exception as e:
            return send_response(False, code=500, message=str(e))

    def update_slo_sle_template(self, template_id, template):
        """
        Modifica (reemplaza) un template SLO/SLE específico
        """
        try:
            xpath = f"/ietf-network-slice-service:network-slice-services/slo-sle-templates/slo-sle-template[id='{template_id}']"
            
            # Verificar que el template existe
            existing_template = get_data_store(xpath)
            if not existing_template:
                return send_response(False, code=404, message="Template not found")
            
            # Asegurar que el ID en el body coincide con el de la URL
            if "id" in template and template["id"] != template_id:
                return send_response(False, code=400, message="Template ID in body does not match URL")
            
            slices = get_data_store("/ietf-network-slice-service:network-slice-services/slice-service")

            for slice in slices["network-slice-services"]["slice-service"]:
                if "slo-sle-template" in slice:
                    if slice.get("slo-sle-template") == template_id:
                        full_intent = {
                            "ietf-network-slice-service:network-slice-services": {
                                "slo-sle-templates": {
                                    "slo-sle-template": [existing_template["network-slice-services"]["slo-sle-templates"]["slo-sle-template"][template_id]]
                                },
                                "slice-service": [slice]
                            }
                        }
                        full_intent = normalize_libyang_data(full_intent)
                        result = self.slice_service.nsc(full_intent, slice.get("id"))
                        if not result:
                            return send_response(False, code=500, message="Slice not updated")

            # Eliminar el ID del body si existe (ya está en el predicado)
            template_data = template.copy()
            template_data.pop("id", None)
            
            # Reemplazar el template
            update_data_store(template_data, xpath)
            logging.info(f"Template {template_id} modified successfully")
            
            return send_response(
                True,
                code=200,
                message="Template updated successfully",
                data=result
            )
        
        except ValueError as e:
            return send_response(False, code=404, message=str(e))
        except Exception as e:
            return send_response(False, code=500, message=str(e))

    def update_slice_service(self, slice_id, intent):
        """
        Modifica (reemplaza) un slice service específico
        """
        try:
            xpath = f"/ietf-network-slice-service:network-slice-services/slice-service[id='{slice_id}']"
            
            # Verificar que el slice existe
            existing_slice = get_data_store(xpath)
            if not existing_slice:
                return send_response(False, code=404, message="Slice not found")
            
            # Asegurar que el ID en el body coincide con el de la URL
            if "id" in intent and intent["id"] != slice_id:
                return send_response(False, code=400, message="Slice ID in body does not match URL")
            
            # Validar que existe el template SLO/SLE referenciado
            if "slo-sle-template" in intent:
                template_ref = intent.get("slo-sle-template")
                xpath_template = f"/ietf-network-slice-service:network-slice-services/slo-sle-templates/slo-sle-template[id='{template_ref}']"
                existing_template = get_data_store(xpath_template)
                if not existing_template:
                    return send_response(False, code=404, message="Referenced SLO/SLE template not found")
                
                # Construir el intent completo para TFS
                full_intent = {
                    "ietf-network-slice-service:network-slice-services": {
                        "slo-sle-templates": {
                            "slo-sle-template": [existing_template["network-slice-services"]["slo-sle-templates"]["slo-sle-template"][template_ref]]
                        },
                        "slice-service": [intent]
                    }
                }
            elif "service-slo-sle-policy" in intent:
                full_intent = {
                    "ietf-network-slice-service:network-slice-services": {
                        "slo-sle-templates": {
                            "slo-sle-template": intent.get("service-slo-sle-policy")
                        },
                        "slice-service": [intent]
                    }
                }
            else:
                return send_response(False, code=400, message="No SLO/SLE template or policy provided in intent")
            
            full_intent = normalize_libyang_data(full_intent)
            result = self.slice_service.nsc(full_intent)
            if not result:
                return send_response(False, code=500, message="Slice not updated")
            
            # Eliminar el ID del body (ya está en el predicado)
            intent_data = intent.copy()
            intent_data.pop("id", None)
            
            # Reemplazar el slice
            update_data_store(intent_data, xpath)
            logging.info(f"Slice {slice_id} modified successfully")
            
            return send_response(
                True,
                code=200,
                message="Slice updated successfully",
                data=result
            )
        
        except ValueError as e:
            return send_response(False, code=404, message=str(e))
        except RuntimeError as e:
            return send_response(False, code=200, message=str(e))
        except Exception as e:
            return send_response(False, code=500, message=str(e))

    def update_sdp(self, slice_id, sdp_id, sdp):
        """
        Modifica (reemplaza) un SDP específico dentro de un slice
        """
        try:
            # Verificar que el slice existe
            slice_xpath = f"/ietf-network-slice-service:network-slice-services/slice-service[id='{slice_id}']"
            existing_slice = get_data_store(slice_xpath)
            if not existing_slice:
                return send_response(False, code=404, message="Slice not found")
            
            # Verificar que el SDP existe
            sdp_xpath = f"/ietf-network-slice-service:network-slice-services/slice-service[id='{slice_id}']/sdps/sdp[id='{sdp_id}']"
            existing_sdp = get_data_store(sdp_xpath)
            if not existing_sdp:
                return send_response(False, code=404, message="SDP not found")
            
            # Asegurar que el ID en el body coincide con el de la URL
            if "id" in sdp and sdp["id"] != sdp_id:
                return send_response(False, code=400, message="SDP ID in body does not match URL")
            
            # Eliminar el ID del body (ya está en el predicado)
            sdp_data = sdp.copy()
            sdp_data.pop("id", None)
            
            # Reemplazar el SDP
            update_data_store(sdp_data, sdp_xpath)
            logging.info(f"SDP {sdp_id} in slice {slice_id} modified successfully")
            
            return send_response(
                True,
                code=200,
                message="SDP updated successfully"
            )
        
        except ValueError as e:
            return send_response(False, code=404, message=str(e))
        except Exception as e:
            return send_response(False, code=500, message=str(e))
     
    ### DELETE

    def delete_network_slice_services(self):
        try:
            # Delete specific slice if slice_id is provided
            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_type = list(slice["service-tags"]["tag-type"]["ietf-network-slice-service:service"]["tag-type-value"])[0]
                    if not slice_type:
                        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"))
                if current_app.config["TFS_L2VPN_SUPPORT"]:
                    self.slice_service.tfs_l2vpn_delete()
            
            delete_data_store(xpath)
            logging.info("All slices removed successfully")

            return {}, 204
        
        except ValueError as e:
@@ -424,6 +661,16 @@ class Api:
                existing_slice = get_data_store(xpath)
                if not existing_slice:
                    raise ValueError("Slice not found")
                if not current_app.config["DUMMY_MODE"]:
                    slice_type = list(existing_slice["network-slice-services"]["slice-service"][slice_id]["service-tags"]["tag-type"]["ietf-network-slice-service:service"]["tag-type-value"])[0]
                    if not slice_type:
                        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"))
                if current_app.config["TFS_L2VPN_SUPPORT"]:
                    self.slice_service.tfs_l2vpn_delete()                

                delete_data_store(xpath)
                logging.info(f"Slice {slice_id} removed successfully")
                return {}, 204
@@ -431,6 +678,17 @@ class Api:
            # Delete all slices
            else:
                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_type = list(slice["service-tags"]["tag-type"]["ietf-network-slice-service:service"]["tag-type-value"])[0]
                        if not slice_type:
                            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"))
                    if current_app.config["TFS_L2VPN_SUPPORT"]:
                        self.slice_service.tfs_l2vpn_delete()
                delete_data_store(xpath)
                logging.info("All slices removed successfully")
                return {}, 204
+5 −3
Original line number Diff line number Diff line
@@ -32,9 +32,11 @@ def store_data(intent, slice_id, controller_type=None):
    """
    # Update or add new slice intent
    if slice_id:
        if controller_type != "RESTCONF": 
            update_data(slice_id, intent, controller_type)
        
    else:
        # Add new slice intent
        slice_id = intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["id"]
        #save_data(slice_id, intent, controller_type)
        create_data_store(intent, xpath=f"/ietf-network-slice-service:network-slice-services/slice-service[id='{slice_id}']")
 No newline at end of file
        if controller_type != "RESTCONF": 
            save_data(slice_id, intent, controller_type)
 No newline at end of file
+140 −30

File changed.

Preview size limit exceeded, changes collapsed.

+1 −1
Original line number Diff line number Diff line
@@ -283,5 +283,5 @@ def create_ietf_network_slice_nbi_yang_model(slice_ns):
        slice_service_model,
        slo_sle_template_model,
        sdp_model,
        NetworkSliceServicesFull
        common_template
    )
+42 −4
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ restconf_ns = Namespace(
    "restconf",
    description="RESTCONF operations for IETF Network Slice Service YANG model"
)
ietf_network_slice_service_response, slice_service_response, slo_sle_template_response, sdp_response, slice_response_model, slice_service_model, slo_sle_template_model, sdp_model, ietf_network_slice_service_model = create_ietf_network_slice_nbi_yang_model(restconf_ns)
ietf_network_slice_service_response, slice_service_response, slo_sle_template_response, sdp_response, slice_response_model, slice_service_model, slo_sle_template_model, sdp_model, common_template = create_ietf_network_slice_nbi_yang_model(restconf_ns)

@restconf_ns.route("/data/ietf-network-slice-service:network-slice-services")
class NetworkSliceServices(Resource):
@@ -23,7 +23,7 @@ class NetworkSliceServices(Resource):
        return Api(controller).get_network_slice_services()

    @restconf_ns.doc(summary="Create Network Slice Services")
    @restconf_ns.expect(ietf_network_slice_service_model)
    @restconf_ns.expect(common_template)
    @restconf_ns.response(201, "Container network-slice-services created", slice_response_model)
    @restconf_ns.response(500, "Internal server error")
    def post(self):
@@ -31,6 +31,16 @@ class NetworkSliceServices(Resource):
        controller = NSController(controller_type="RESTCONF")
        return Api(controller).add_network_slice_service(json_data)

    @restconf_ns.doc(summary="Update Network Slice Services")
    @restconf_ns.expect(common_template)
    @restconf_ns.response(200, "Container network-slice-services updated", slice_response_model)
    @restconf_ns.response(404, "Nothing found to update")
    @restconf_ns.response(500, "Internal server error")
    def put(self):
        json_data = request.get_json()
        controller = NSController(controller_type="RESTCONF")
        return Api(controller).update_network_slice_service(json_data)

    @restconf_ns.doc(summary="Delete Network Slice Services")
    @restconf_ns.response(204, "All slices deleted")
    @restconf_ns.response(500, "Internal server error")
@@ -49,7 +59,8 @@ class SliceServiceList(Resource):
        return Api(controller).get_slice_services()

    @restconf_ns.doc(summary="Create Slice Services")
    @restconf_ns.expect(slice_service_model)
    @restconf_ns.expect(common_template)
    #@restconf_ns.expect(slice_service_model)
    @restconf_ns.response(201, "Slice created", slice_response_model)
    @restconf_ns.response(409, "Slice already exists")
    def post(self):
@@ -74,6 +85,15 @@ class SliceService(Resource):
        controller = NSController(controller_type="RESTCONF")
        return Api(controller).get_slice_services(slice_service_id)
    
    @restconf_ns.doc(summary="Update Slice Service by ID")
    @restconf_ns.expect(common_template)
    @restconf_ns.response(200, "Slice updated", slice_response_model)
    @restconf_ns.response(404, "No slice found to update")
    def put(self, slice_service_id):
        json_data = request.get_json()
        controller = NSController(controller_type="RESTCONF")
        return Api(controller).update_slice_service(slice_service_id, json_data)

    @restconf_ns.doc(summary="Delete a Slice Service by ID")
    @restconf_ns.response(204, "Slice deleted")
    @restconf_ns.response(404, "Slice not found")
@@ -115,6 +135,15 @@ class SloSleTemplate(Resource):
        controller = NSController(controller_type="RESTCONF")
        return Api(controller).get_slo_sle_templates(slo_sle_template_id)
    
    @restconf_ns.doc(summary="Update SLO/SLE template by ID")
    @restconf_ns.expect(slo_sle_template_model)
    @restconf_ns.response(200, "Template updated", slice_response_model)
    @restconf_ns.response(404, "No template found to update")
    def put(self, slo_sle_template_id):
        json_data = request.get_json()
        controller = NSController(controller_type="RESTCONF")
        return Api(controller).update_slo_sle_template(slo_sle_template_id, json_data)

    @restconf_ns.doc(summary="Delete SLO/SLE template by ID")
    @restconf_ns.response(204, "Template deleted")
    def delete(self, slo_sle_template_id):
@@ -131,7 +160,8 @@ class SdpList(Resource):
        return Api(controller).get_sdps(slice_service_id)

    @restconf_ns.doc(summary="Create Slice Service SDPs")
    @restconf_ns.expect(sdp_model)
    @restconf_ns.expect(common_template)
    #@restconf_ns.expect(sdp_model)
    @restconf_ns.response(201, "SDP created", slice_response_model)
    @restconf_ns.response(409, "SDP already exists")
    def post(self, slice_service_id):
@@ -153,6 +183,14 @@ class Sdp(Resource):
    def get(self, slice_service_id, sdp_id):
        controller = NSController(controller_type="RESTCONF")
        return Api(controller).get_sdps(slice_service_id, sdp_id)
    @restconf_ns.doc(summary="Update Slice Service SDP by ID")
    @restconf_ns.expect(common_template)
    @restconf_ns.response(200, "SDP updated", slice_response_model)
    @restconf_ns.response(404, "No SDP found to update")
    def put(self, slice_service_id, sdp_id):
        json_data = request.get_json()
        controller = NSController(controller_type="RESTCONF")
        return Api(controller).update_sdp(slice_service_id, sdp_id, json_data)
    @restconf_ns.doc(summary="Delete Slice Service SDP by ID")
    @restconf_ns.response(204, "SDP deleted")
    def delete(self, slice_service_id, sdp_id):