diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/EstructuraIntermedia.json b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/EstructuraIntermedia.json new file mode 100644 index 0000000000000000000000000000000000000000..e57011be614cc6af8bc011b4fd8581e86f0856c5 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/EstructuraIntermedia.json @@ -0,0 +1,231 @@ +{ + "l3vpn-svc": { + "vpn-services": { + "vpn-service": [ + { + "vpn-id": "vpn2" + } + ] + }, + "sites": { + "site": [ + { + "site-id": "site_OLT", + "locations": { + "location": [ + { + "location-id": "OLT" + } + ] + }, + "devices": { + "device": [ + { + "device-id": "128.32.33.5", + "location": "OLT" + } + ] + }, + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": "ietf-l3vpn-svc:static", + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "128.32.10.0/24", + "next-hop": "128.32.33.2", + "lan-tag": "vlan31" + }, + { + "lan": "128.32.20.0/24", + "next-hop": "128.32.33.2", + "lan-tag": "vlan31" + } + ] + } + } + } + ] + }, + "site-network-accesses": { + "site-network-access": [ + { + "site-network-access-id": "500", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "device-reference": "128.32.33.5", + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "provider-address": "128.32.33.254", + "customer-address": "128.32.33.2", + "prefix-length": 24 + } + } + }, + "service": { + "svc-input-bandwidth": 1000000000, + "svc-output-bandwidth": 1000000000, + "svc-mtu": 1500, + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 10 + }, + "bandwidth": { + "guaranteed-bw-percent": 100.0 + } + } + ] + } + } + } + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": "ietf-l3vpn-svc:static", + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.1.201.0/24", + "next-hop": "128.32.33.254", + "lan-tag": "vlan31" + } + ] + } + } + } + ] + }, + "vpn-attachment": { + "vpn-id": "vpn2", + "site-role": "ietf-l3vpn-svc:spoke-role" + } + } + ] + } + }, + { + "site-id": "site_POP", + "locations": { + "location": [ + { + "location-id": "POP" + } + ] + }, + "devices": { + "device": [ + { + "device-id": "172.10.33.5", + "location": "POP" + } + ] + }, + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": "ietf-l3vpn-svc:static", + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.1.201.0/24", + "next-hop": "172.10.33.2", + "lan-tag": "vlan201" + } + ] + } + } + } + ] + }, + "site-network-accesses": { + "site-network-access": [ + { + "site-network-access-id": "500", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "device-reference": "172.10.33.5", + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "provider-address": "172.10.33.254", + "customer-address": "172.10.33.2", + "prefix-length": 24 + } + } + }, + "service": { + "svc-input-bandwidth": 1000000000, + "svc-output-bandwidth": 1000000000, + "svc-mtu": 1500, + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 10 + }, + "bandwidth": { + "guaranteed-bw-percent": 100.0 + } + } + ] + } + } + } + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": "ietf-l3vpn-svc:static", + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "128.32.10.0/24", + "next-hop": "172.10.33.254", + "lan-tag": "vlan201" + }, + { + "lan": "128.32.20.0/24", + "next-hop": "172.10.33.254", + "lan-tag": "vlan201" + } + ] + } + } + } + ] + }, + "vpn-attachment": { + "vpn-id": "vpn2", + "site-role": "ietf-l3vpn-svc:hub-role" + } + } + ] + } + } + ] + } + } +} \ No newline at end of file diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py index 80c7b32ddf6cabf8a6c124ec20ddae2f5cd181ad..72e042760b4a3d1b3437d495d89392fc77564ea5 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging, netaddr +import logging, netaddr, json from typing import Dict, List, Optional, Tuple from common.Constants import DEFAULT_CONTEXT_NAME from common.proto.context_pb2 import Service, ServiceStatusEnum, ServiceTypeEnum @@ -26,6 +26,9 @@ from common.tools.grpc.EndPointIds import update_endpoint_ids from context.client.ContextClient import ContextClient from service.client.ServiceClient import ServiceClient +from .ReadServiceFunctionDefinition import ReadServiceFunctionDefinition +from .WriteNetworkFunctionDefinition import WriteNetworkFunctionDefinition + LOGGER = logging.getLogger(__name__) def create_service( @@ -209,3 +212,55 @@ def process_site(site : Dict, errors : List[Dict]) -> None: network_accesses : List[Dict] = site['site-network-accesses']['site-network-access'] for network_access in network_accesses: process_site_network_access(site_id, network_access, site_static_routing, errors) + + +def convert_l3sm_to_l3nm(): + jsonRequest = open("/home/ubuntu/tfs-ctrl/src/nbi/tests/ietf_l3vpn_req_svc1.json", "r") #devuelve un file-object. "r" es por reading + output_file_path = "/home/ubuntu/tfs-ctrl/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/EstructuraIntermedia.json" + jsonRequestData : str = jsonRequest.read() #jsonRequestData es de tipo str. Para leer el contenido, usamos read() + + jParse : Dict = json.loads(jsonRequestData) #jParse es de tipo Dict. esto va a parsear jsonRequetData. + #cuando obtengamos el valor de una clave, y ese valor sea un array [], el tipo será 'list' + #in order to convert a jsonObject to an string, you can use .dumps + + ietf_l3vpn : Dict = jParse['ietf-l3vpn-svc:l3vpn-svc'] + + vpn_services : Dict = ietf_l3vpn['vpn-services'] + #print(vpn_services) + # vpn_service = vpn_services['vpn-service'] #ahora, al ser vpn-service un array, creo que la variable vpn_service es de tipo 'list' + + # print(type(vpn_service)) #list + # print("VPN_SERVICE: " + str(vpn_service)) + # print(vpn_service[0]) + # print(vpn_service[0]['vpn-id']) + + bd : Dict[any, any] = {} #Base datos de variables del JSON + + #vpn_id = MiClase.readVpnServices(vpn_services) #vpn_id es variable de tipo List[str] con todas las vpn posibles + + #try: + bd = ReadServiceFunctionDefinition.writeVpnServices( ReadServiceFunctionDefinition.readVpnServices(vpn_services) , bd) + #print(json.dumps(bd, indent=2)) + + sites : List = ietf_l3vpn['sites']['site'] #En sites se almacena el valor de la clave 'sites'. + #print(json.dumps(sites, indent=2)) + + #### PEQUEÑA CHAPUZA PARA CUANDO SOLO HAY UNA VPN #### + lista_VPNs : list = bd["VPN_IDs"] + bd["VPNs_Dict"][lista_VPNs[0]].update(ReadServiceFunctionDefinition.readSiteArray(sites, bd)) #vpn1.update Updatea el valor de vpn1 + #### # #### + + + # except Exception as e: #En principio llegaran valueError o NoJSONFieldException + # print(e) + + #print(json.dumps(bd, indent=2)) + + #YANG SUITE ENTRADA, SERVICE ->creo que IETF-NBI-TEF-DECEMBER-23; ietf-l3vpn-svc; edit-config; none selected + #YANG SUITE SALIDA, RED (RFC9182) -> IETF-NBI-TEF-DECEMBER-23; ietf-l3vpn-ntw; edit-config; none selected + #Codigo para crear archivo .json de salida + with open(output_file_path, 'w') as json_file: + json.dump(bd, json_file, indent=3) + + jsonL3VPN_net : Dict = WriteNetworkFunctionDefinition.write() + # print(json.dumps(jsonL3VPN_net, indent = 3)) diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Services.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Services.py index 22f01031c3d994d859314f83fae56379c2a4cba3..6bd57c8238c1af63ed3f504593f3c70cf8a68cc6 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Services.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Services.py @@ -11,9 +11,8 @@ # 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. - -import json, logging +import logging from typing import Dict, List from flask import request from flask.json import jsonify @@ -24,11 +23,6 @@ from nbi.service.rest_server.nbi_plugins.tools.Authentication import HTTP_AUTH from .Handlers import process_site, process_vpn_service from .YangValidator import YangValidator -# from FunctionDefinition import FunctionDefinition -# from FunctionDefinitionNetwork import FunctionDefinitionNetwork -# import FunctionDefinition -# import FunctionDefinitionNetwork - LOGGER = logging.getLogger(__name__) class L3VPN_Services(Resource): @@ -38,16 +32,9 @@ class L3VPN_Services(Resource): @HTTP_AUTH.login_required def post(self): - - jsonAValidar = open("/home/ubuntu/Documents/json1.json", "r") - jsonAValidarData : str = jsonAValidar.read() - jsonAValidarDict : Dict = json.loads(jsonAValidarData) - output_file_path = "/home/ubuntu/Documents/EstructuraIntermedia.json" - if not request.is_json: raise UnsupportedMediaType('JSON payload is required') request_data : Dict = request.json - # LOGGER.debug('Request: {:s}'.format(str(request_data))) - LOGGER.debug('Request: {:s}'.format(str(jsonAValidarDict))) + LOGGER.debug('Request: {:s}'.format(str(request_data))) errors = list() if 'ietf-l3vpn-svc:l3vpn-services' in request_data: @@ -79,53 +66,15 @@ class L3VPN_Services(Resource): def _process_l3vpn(self, request_data : Dict) -> List[Dict]: yang_validator = YangValidator('ietf-l3vpn-svc') - request_data = yang_validator.parse_to_dict(jsonAValidarDict) + request_data = yang_validator.parse_to_dict(request_data) yang_validator.destroy() - #se supone que a partir de aqui ya ha validado - - jParse = jsonAValidarDict - - ietf_l3vpn : Dict = jParse['ietf-l3vpn-svc:l3vpn-svc'] - - vpn_services : Dict = ietf_l3vpn['vpn-services'] - - bd : Dict[any, any] = {} #Base datos de variables del JSON - - bd = FunctionDefinition.writeVpnServices( FunctionDefinition.readVpnServices(vpn_services) , bd) - - sites : List = ietf_l3vpn['sites']['site'] #En sites se almacena el valor de la clave 'sites'. - - #### FUNCIONA SOLO CON 1 VPN #### - lista_VPNs = bd["VPN_IDs"] - bd["VPNs_Dict"][lista_VPNs[0]].update(FunctionDefinition.readSiteArray(sites, bd)) - #### --------------------- #### - - with open(output_file_path, 'w') as json_file: - json.dump(bd, json_file, indent=3) - - jsonL3VPN_net : Dict = FunctionDefinitionNetwork.write() - # print(json.dumps(jsonL3VPN_net, indent = 3)) - - - - - - - - - ###### - - errors = [] + errors = list() - # for vpn_service in request_data['l3vpn-svc']['vpn-services']['vpn-service']: - # process_vpn_service(vpn_service, errors) + for vpn_service in request_data['l3vpn-svc']['vpn-services']['vpn-service']: + process_vpn_service(vpn_service, errors) - # for site in request_data['l3vpn-svc']['sites']['site']: - # process_site(site, errors) + for site in request_data['l3vpn-svc']['sites']['site']: + process_site(site, errors) - # response = jsonify(errors) - # response.status_code = HTTP_CREATED if len(errors) == 0 else HTTP_SERVERERROR - # return response - return errors diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/ReadServiceFunctionDefinition.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/ReadServiceFunctionDefinition.py new file mode 100644 index 0000000000000000000000000000000000000000..3218a18ddd2d5205c170723b2adaaadf323caddd --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/ReadServiceFunctionDefinition.py @@ -0,0 +1,418 @@ +from typing import Dict, List + +class ReadServiceFunctionDefinition: + + #Metodo para leer el campo vpn-services + @staticmethod + def readVpnServices(vpnServicesALeer: Dict) -> List: + # Acceder al campo 'vpn-service' que es un array de Dict con un campo vpn-id + vpn_service_list = vpnServicesALeer.get('vpn-service', [])#Utilizar get para manejar casos donde 'vpn-service' no está presente + vpn_ids = [] #Array con todos los ID de cada VPN + + # Iterar sobre los elementos de la lista + for vpn_serviceEnFor in vpn_service_list: + vpn_id : str = vpn_serviceEnFor.get('vpn-id') #obtengo vpn-id del json iterando + # baseDatos["vpn-id_" + str(i)] = vpn_id # Utilizar f-strings para formatear las claves + vpn_ids.append(vpn_id) + + return vpn_ids + + #Metodo para escribir en la baseDatos lo leido en vpn-services + @staticmethod + def writeVpnServices(listaVPNs : List, baseDatos : Dict) -> Dict: + + baseDatos["VPN_IDs"] = listaVPNs + #baseDatos.update({"VPN_IDs": vpn_ids}) #ambas lineas son validas para añadir al Dict + baseDatos["VPNs_Dict"] = {} + #Obtengo cada vpn-id del array y lo meto en VPNs_Dict + for clave in baseDatos["VPN_IDs"]: + baseDatos["VPNs_Dict"][clave] = {}#ya tengo un Dict asociado a cada clave + + return baseDatos #devuelve la baseDatos pasada por argumento pero actualizada + + #Lee el array site + @staticmethod + def readSiteArray(site: List, baseDatos: Dict) -> Dict: + print("Elementos en site: " + str(len(site))) + temp : Dict = {} #Diccionario temporal que guarda el ultimo site leido + temp2 : Dict = {} #Diccionario temporal que registra todos los sites leidos + lista_SitesID = [] #lista que almacenara los IDs de cada site del array + i = 1 + for siteElement in site: + temp = ReadServiceFunctionDefinition.readSiteElement(lista_SitesID, siteElement) #Leo un site y me quedo con ID y Dict + temp2.update({lista_SitesID[-1] : temp}) # Almaceno esos datos en temp2 + i += 1 + + res : Dict = {} + res["sitesIDs"] = lista_SitesID + res["sites_Dict"] = temp2 # A sites_Dict se le añaden todos los sites + return res + + #Lee un site + @staticmethod + def readSiteElement(listaID: List, siteElement: Dict) -> Dict: + res: Dict = {} + listaID.append(siteElement.get('site-id')) + res.update({"site-id": siteElement.get('site-id')}) + res.update({"management": siteElement.get("management")}) #revistar si está bien + res.update({"locations":siteElement.get("locations")}) + res.update({"devices": ReadServiceFunctionDefinition.readDevices(siteElement.get('devices'))}) + res.update({"routing-protocol" : ReadServiceFunctionDefinition.readRoutingProtocols(siteElement.get('routing-protocols'))}) + res.update({"site-network-accesses" : ReadServiceFunctionDefinition.readSiteNetworkAccesses(siteElement.get('site-network-accesses'))}) + print("readSiteElement Finiquitaoooo") + return res + #FALTAN MUCHOS CAMPOS A LEER EN site, como site-diversity, maximum-routes, security, service, traffic-protection + + #Lee array de Devices. Si hay más de un device, cambiar código + @staticmethod + def readDevices(devices: Dict) -> Dict: + res: Dict = {} + devices_list: List = devices.get('device', []) # Cambié 'devices' por 'device' aquà + if len(devices_list) > 1: + i: int = 1 + for eachDevice in devices_list: + res.update({f"device-id {i}": eachDevice.get('device-id')}) + i += 1 + elif len(devices_list) == 1: + res.update({"device-id": devices_list[0].get('device-id')}) + return res + + @staticmethod + def readRoutingProtocols(routingProtocols : Dict) -> Dict: + res : Dict = {} #todo el dict routing-protocols + rpArray : List = routingProtocols.get("routing-protocol") #el array routing-protocol + res.update({"type":rpArray[0].get("type")})#escribo el tipo en el diccionario + tipoRoutingProtocol : str = rpArray[0].get("type")#obtiene string "ietf-l3vpn-svc:static" + tipoResumido : str = tipoRoutingProtocol.split(":")[1] #se queda con la palabra, ospf, bgp, static,... sin el ietf... + #print("El tipo de routingProtocol es: "+ str(tipoResumido)) + def case1(): + ospfEntrada : Dict = rpArray[0].get("ospf",{}) #Funciona solo en caso de que el array tenga un elemento + ospfIntermedio : Dict = {} + ospfIntermedio.update({"address-family":ospfEntrada.get("address-family",{})}) + ospfIntermedio.update({"area-address":ospfEntrada.get("area-address",{})}) + ospfIntermedio.update({"metric":ospfEntrada.get("metric",{})}) + arrayShamLinksEntrada : List = ospfEntrada.get("sham-links",[]) #el array + shamLinksFinal : Dict = {} + arrayShamLinksFinal : List = [] + for elemento in arrayShamLinksEntrada: + shamLinksFinal.update({"target-site":elemento.get("target-site")}) + shamLinksFinal.update({"metric":elemento.get("metric")}) + arrayShamLinksFinal.append(shamLinksFinal) + + ospfIntermedio.update({"sham-links":shamLinksFinal}) + + res.update({"ospf":ospfIntermedio}) + + def case2(): + bgpEntrada : Dict = rpArray[0].get("bgp",{}) + bgpSalida : Dict = {} + bgpSalida.update({"autonomous-system":bgpEntrada.get("autonomous-system")}) + bgpSalida.update({"address-family":bgpEntrada.get("address-family")}) + res.update({"bgp":bgpSalida}) + + def case3(): #SIMPLEMENTE COPIA, NO LEE. A IMPLEMENTAR + cascadedDict: Dict = rpArray[0].get("static", {}).get("cascaded-lan-prefixes", {}) + valueRoutingProtocol = cascadedDict + res.update({"static":valueRoutingProtocol}) + print("se ha copiado 'static' pero no se ha leido, falta implementar") + + def case4(): + ripEntrada : Dict = rpArray[0].get("rip",{}) + ripSalida : Dict = {} + ripSalida.update({"address-family":ripEntrada.get("address-family")}) + res.update({"rip":ripSalida}) + + def case5(): + vrrpEntrada : Dict = rpArray[0].get("vrrp",{}) + vrrpSalida : Dict = {} + vrrpSalida.update({"address-family":vrrpEntrada.get("address-family")}) + res.update({"vrrp":vrrpSalida}) + + def default(): + #return ValueError(f"Tipo de protocolo no compatible: {tipoResumido}") + print("TIPO DE PROTOCOLO NO VALIDO") + + switch = { + "ospf": case1, + "bgp": case2, + "static": case3, + "rip":case4, + "vrrp": case5 + } + #res.update({tipoResumido: switch.get(tipoResumido, default)()}) + switch.get(tipoResumido)() + # print("_______________________d____________"+str(res)) + print("read Routing Protocols finished") + return res + + + @staticmethod + def readSiteNetworkAccesses(siteNetworkAccesses : Dict) -> Dict: + siteNetworkAccessArray : List = siteNetworkAccesses.get("site-network-access") + res : Dict = {} + arrayDeIDs : List = [] + for siteNetworkAccess in siteNetworkAccessArray: + variableIncial : str = siteNetworkAccess.get("site-network-access-id") + variableIncial = variableIncial.replace(" ", "-")#ESTO LO HAGO PARA QUE NO HAYA PROBLEMAS CON LOS ESPACIOS A LA HORA DE LEER EL JSON + arrayDeIDs.append(variableIncial) + res.update({"site-network-accesses-IDs":arrayDeIDs}) + dictSiteNetworkAccesses : Dict = {} + + for site in arrayDeIDs: + dictDeCadaID : Dict = {} + + # try: + dictDeCadaID.update({"site-network-access-type":siteNetworkAccess.get("site-network-access-type")}) + dictDeCadaID.update({"device-reference":siteNetworkAccess.get("device-reference")}) + dictDeCadaID.update({"site-role":siteNetworkAccess.get("vpn-attachment", {}).get("site-role")}) + #dictDeCadaID.update({"location-flavor" : FunctionDefinition.readLocationFlavor(siteNetworkAccess.get("location-flavor"))}) DE MOMENTO DA ERROR PORQUE ESE CAMPO NO EXISTE + #dictDeCadaID.update({"access-diversity" : FunctionDefinition.readAccessDiversity(siteNetworkAccess.get("access-diversity"))}) CAMPO NO EXISTE AUN + #dictDeCadaID.update({"bearer" : FunctionDefinition.readBearer(siteNetworkAccess.get("bearer"))}) CAMPO NO EXISTE AUN + dictDeCadaID.update({"ip-connection":ReadServiceFunctionDefinition.readIpConnection(siteNetworkAccess.get("ip-connection"))}) + dictDeCadaID.update({"routing-protocols":ReadServiceFunctionDefinition.readRoutingProtocols(siteNetworkAccess.get("routing-protocols"))}) + dictDeCadaID.update({"service":ReadServiceFunctionDefinition.readService(siteNetworkAccess.get("service"))}) + # except NoJSONFieldException as e: + # print(e) + + dictSiteNetworkAccesses.update({site:dictDeCadaID}) + print("readSiteNetworkAccesses finiquitaoo") + res.update({"site-network-accesses-Dict": dictSiteNetworkAccesses}) + return res + + @staticmethod + def readLocationFlavor(locationFlavor : Dict) -> Dict: + res : Dict = {} + + if "location" in locationFlavor: + res.update({"location-reference":locationFlavor.get("location", {}).get("location-reference")}) + elif "device" in locationFlavor: + res.update({"device-reference":locationFlavor.get("device", {}).get("device-reference")}) + # else: + # raise NoJSONFieldException("location-flavor") + + return res + + @staticmethod + def readAccessDiversity(accessDiversity : Dict) -> Dict: + res : Dict = {} + arrayGroupsIni : List = accessDiversity.get("groups",{}).get("group") #obtengo el array de objetos + arrayGroupsIDs : List = [] + for groupId in arrayGroupsIni: + arrayGroupsIDs.append(groupId.get("group-id")) + res.update({"groups":arrayGroupsIDs}) + + arrayConstraintsIni : List = accessDiversity.get("constraints", {}).get("constraint") + arrayConstraintsFin : List = [] + for constraint in arrayConstraintsIni: #recordar que constraint es un objeto dentro del array arrayConstraintsIni + dictConstraint : Dict = {} + dictConstraint.update({"constraint-type" : constraint.get("constraint-type")}) + dictTargetFlavor : Dict = constraint.get("target", {}).get("target-flavor") + + if "id" in dictTargetFlavor: + dictConstraint.update({"target-flavor": dictTargetFlavor.get("id")}) + elif "all-accesses" in dictTargetFlavor: + dictConstraint.update({"target-flavor": dictTargetFlavor.get("all-accesses")}) + elif "all-groups" in dictTargetFlavor: + dictConstraint.update({"target-flavor": dictTargetFlavor.get("all-groups")}) + # else: + # raise NoJSONFieldException("read-access-divesity>constraints>constraint>target>target-flavor") + arrayConstraintsFin.append(dictConstraint) + + res.update({"constraints":arrayConstraintsFin}) + return res + + @staticmethod + def readBearer(bearer : Dict) -> Dict: + res : Dict = {} + + res.update({"always-on":bearer.get("always-on")}) + res.update({"bearer-reference":bearer.get("bearer-reference")}) + res.update({"requested-type":bearer.get("requested-type",{}).get("requested-type")}) + res.update({"strict":bearer.get("requested-type",{}).get("strict")}) + + return res + + @staticmethod + def readIpConnection(ipConnection : Dict) -> Dict: #IPV4 E IPV6 TIENEN LOS MISMOS CAMPOS + res : Dict = {} + if ipConnection.get("ipv4") is not None: + res.update({"ipv4": ReadServiceFunctionDefinition.readIpv4_Ipv6(ipConnection.get("ipv4"))}) + elif ipConnection.get("ipv6") is not None: + res.update({"ipv6":ReadServiceFunctionDefinition.readIpv4_Ipv6(ipConnection.get("ipv6"))}) + # else: + # raise NoJSONFieldException("ip-connection>ipv4/6") + + bfd : Dict = ipConnection.get("oam",{}).get("bfd") + if bfd is not None: + oamFinal : Dict = {} + oamFinal.update({"enabled":bfd.get("enabled")}) + holdtime : Dict = bfd.get("holdtime") + holdtimeFinal : Dict = {} + if "fixed" in holdtime: + holdtimeFinal.update({"fixed":holdtime.get("fixed")}) + elif "profile" in holdtime: + holdtimeFinal.update({"profile":holdtime.get("profile")}) + # else: + # raise NoJSONFieldException("oam>bfd>holdtime") + + oamFinal.update({"holdtime":holdtimeFinal}) + res.update({"oam":oamFinal}) + + return res + + @staticmethod + def readIpv4_Ipv6(ipv : Dict) -> Dict : + res : Dict = {} + + res.update({"address-allocation-type":ipv.get("address-allocation-type")}) + + providerDHCP : Dict = ipv.get("provider-dhcp") #None si no existe ese campo, puede mejorarse eficiencia + if providerDHCP is not None: + providerDHCPFinal : Dict = {} + providerDHCPFinal.update({"provider-dhcp":providerDHCP.get("provider-address")}) + providerDHCPFinal.update({"prefix-length":providerDHCP.get("prefix-lenght")}) + addressAsign : Dict = providerDHCP.get("address-asign") + addressAsignFinal : Dict = {} + if "number" in addressAsign: + addressAsignFinal.update({"number":addressAsign.get("number")}) + elif "explicit" in addressAsign: + addressAsignFinal.update({"explicit":addressAsign.get("explicit")}) + # else: + # raise NoJSONFieldException("ipv4/6>address-asign") + + providerDHCPFinal.update({"address-asign":addressAsignFinal}) + res.update({"provider-dhcp":providerDHCPFinal}) + + + dhcpRelay : Dict = ipv.get("dhcp-relay") + if dhcpRelay is not None: + dhcpRelayFinal : Dict = {} + dhcpRelayFinal.update({"provider-address":dhcpRelay.get("provider-address")}) + dhcpRelayFinal.update({"prefix-lenght":dhcpRelay.get("prefix-lenght")}) + dhcpRelayFinal.update({"server-ip-address":dhcpRelay.get("customer-dhcp-servers",{}).get("server-ip-address")}) + res.update({"dhcp-relay":dhcpRelayFinal}) + + addresses : Dict = ipv.get("addresses") + if addresses is not None: + addressesFinal : Dict = {} + addressesFinal.update({"provider-address":addresses.get("provider-address")}) + addressesFinal.update({"customer-address":addresses.get("customer-address")}) + addressesFinal.update({"prefixe-length":addresses.get("prefixe-length")}) + res.update({"addresses":addressesFinal}) + + return res + + # @staticmethod + # def readSecurity(security : Dict) -> Dict: + # res : Dict = {} + + # res.update({"authentication" : security.get("authentication")}) + # encryption : Dict = security.get("encryption") + # encryptionFinal : Dict = {} + + + # return res + + #readService todavÃa me da error + @staticmethod + def readService(service : Dict) -> Dict : + res : Dict = {} + #try: + res.update({"svc-mtu":service.get("svc-mtu")}) + res.update({"svc-input-bandwidth":service.get("svc-input-bandwidth")}) + res.update({"svc-output-bandwidth":service.get("svc-output-bandwidth")}) + qos : Dict = service.get("qos") + + class_id = qos["qos-profile"]["classes"]["class"][0]["class-id"] + direction = qos["qos-profile"]["classes"]["class"][0]["direction"] + latency_boundary = qos["qos-profile"]["classes"]["class"][0]["latency"]["latency-boundary"] + guaranteed_bw_percent = qos["qos-profile"]["classes"]["class"][0]["bandwidth"]["guaranteed-bw-percent"] + + res.update({"class-id":class_id}) + res.update({"direction":direction}) + res.update({"latency-boundary":latency_boundary}) + res.update({"guaranteed-bw-percent":guaranteed_bw_percent}) + + #Clase que lee el campo custom varias lineas mas adelante + + def readCustom (custom : Dict) -> Dict: #custom es un Dict que es: {classes: [class-id: *, ..., latency: {flavor:{lowest:*///boundary:*}}],[...],[...]} + res : Dict = {} + + arrayClassesEntrada : List = custom.get("class") #un array con elementos indeterminados + arrayClassesFinal : List = [] + + for elementEntrada in arrayClassesEntrada: + elementFinal : Dict = {} + elementFinal.update({"class-id":elementEntrada.get("class-id")}) + elementFinal.update({"direction":elementEntrada.get("direction")}) + elementFinal.update({"rate-limit":elementEntrada.get("rate-limit")}) + #Campo latency---> + #latency: { + # flavor:{ + # lowest:* + # o quizás + # boundary:* + # } + # } + + latency : Dict = elementEntrada.get("latency") + jitter : Dict = elementEntrada.get("jitter") + if latency is not None: + if "use-lowest-latency" in latency: + elementFinal.update({"latency-flavor-lowest":latency.get("use-lowest-latency")}) + elif "latency-boundary" in latency: + elementFinal.update({"latency-flavor-boundary":latency.get("latency-boundary")}) + elif jitter is not None: + if "use-lowest-jitter" in jitter: + elementFinal.update({"jitter-flavor-lowest":jitter.get("use-lowest-jiter")}) + elif "latency-boundary" in jitter: + elementFinal.update({"jitter-flavor-boundary":jitter.get("latency-boundary")}) + else: + print("Debe haber un campo lowest o boundary dentro de jitter>flavor") + else: + print("FALTAN CAMPOS EN CUSTOM") + + bandwidthEntrada : Dict = elementEntrada.get("bandwidth") + bandwidthSalida : Dict = {} + bandwidthSalida.update({"guaranteed-bw-percent":bandwidthEntrada.get("guaranteed-bw-percent")}) + bandwidthSalida.update({"end-to-end":bandwidthEntrada.get("end-to-end")}) + elementFinal.update({"bandwidth":bandwidthSalida}) + + arrayClassesFinal.append(elementFinal) + + # res.update({"latency-flavor":arrayClassesFinal}) + # return res + # #END READCUSTOM + + # qosProfileEntrada : Dict = qos.get("qos-profile") + # qosProfileFin : Dict = {} + + # if "profile" in qosProfileEntrada: + # qosProfileFin.update({"standard-profile":qosProfileEntrada.get("standard")}) + # elif "classes" in qosProfileEntrada: + # qosProfileFin.update({"custom-classes":readCustom(qosProfileEntrada.get("classes"))}) + # else: + # print("falta campo en qos>qos-profile") + # #raise NoJSONFieldException("qos>qos-profile") + + # res.update({"qos-profile":qosProfileFin}) + # res.update({"carrierscarrier":qos.get("carrierscarrier")}) + # multicastEntrada : Dict = qos.get("multicast") + # multicastSalida : Dict = {} + # multicastSalida.update({"multicast-site-type":multicastEntrada.get("multicast-site-type")}) + # multicastAddressSalida : Dict = {} + # multicastAddressSalida.update({"ipv4":multicastEntrada.get("multicast-address-family", {}).get("ipv4")}) + # multicastAddressSalida.update({"ipv6":multicastEntrada.get("multicast-address-family", {}).get("ipv6")}) + # multicastSalida.update({"multicast-address-family":multicastAddressSalida}) + # multicastSalida.update({"protocol-type":multicastEntrada.get("protocol-type")}) + + # res.update({"multicast":multicastSalida}) + + # # except KeyError: + # # print("falta alguna clave en service") + # # except NoJSONFieldException as e: + # # print(e) + # # finally: + return res + + \ No newline at end of file diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/FunctionDefinitionNetwork.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/WriteNetworkFunctionDefinition.py similarity index 88% rename from src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/FunctionDefinitionNetwork.py rename to src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/WriteNetworkFunctionDefinition.py index 940fd00c9c1489e2a7d1446b3c32cf4c7d2db10b..5c22de56dee9140467fb747c4d43db2a3ab25bcc 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/FunctionDefinitionNetwork.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/WriteNetworkFunctionDefinition.py @@ -1,22 +1,24 @@ from typing import Dict, List -import json +import json, os -class FunctionDefinitionNetwork: +class WriteNetworkFunctionDefinition: - jsonEstructuraIntermedia = open("/home/ubuntu/Documents/EstructuraIntermedia.json", "r") + input_file_path : str ="/home/ubuntu/Documents/EstructuraIntermedia.json" output_file_path = "/home/ubuntu/Documents/L3VPN_ntw.json" + + jsonEstructuraIntermedia = open(input_file_path, "r") jsonEstructuraIntermediaData : str = jsonEstructuraIntermedia.read() jsonEstIntDict : Dict = json.loads(jsonEstructuraIntermediaData) @staticmethod def numberVpnIDs() -> int : - vpnIDs : List = FunctionDefinitionNetwork.jsonEstIntDict["VPN_IDs"] + vpnIDs : List = WriteNetworkFunctionDefinition.jsonEstIntDict["VPN_IDs"] return len(vpnIDs) #Este metodo devuelve el numero de sites, y es necesario porque el numero de sites es el numero de nodes @staticmethod def numberSitesIDS() -> int : - sitesIDs : List = FunctionDefinitionNetwork.jsonEstIntDict["VPNs_Dict"]["vpn1"]["sitesIDs"] + sitesIDs : List = WriteNetworkFunctionDefinition.jsonEstIntDict["VPNs_Dict"]["vpn1"]["sitesIDs"] return len(sitesIDs) @staticmethod @@ -24,7 +26,7 @@ class FunctionDefinitionNetwork: jsonNet : Dict = {} jsonNet.update({"vpn-service":{}}) #no tenemos en cuenta que puede haber más de 1 VPN. Codigo valido para 1 VPN solo - vpn_id : str = FunctionDefinitionNetwork.jsonEstIntDict["VPN_IDs"][0].split("vpn")[1] #obtengo el numero de vpn_id + vpn_id : str = WriteNetworkFunctionDefinition.jsonEstIntDict["VPN_IDs"][0].split("vpn")[1] #obtengo el numero de vpn_id jsonNet["vpn-service"]["vpn-id"] = vpn_id #de momento solo 1 vpn # jsonNet["vpn-service"]["customer-name"] = "_" # jsonNet["vpn-service"]["parent-service-id"] = "_" @@ -33,24 +35,24 @@ class FunctionDefinitionNetwork: jsonNet["vpn-service"]["vpn-nodes"] = {} arrayVpnNode : List = [] - print(FunctionDefinitionNetwork.numberSitesIDS()) - for numeroDeNode in range(FunctionDefinitionNetwork.numberSitesIDS()): + print(WriteNetworkFunctionDefinition.numberSitesIDS()) + for numeroDeNode in range(WriteNetworkFunctionDefinition.numberSitesIDS()): - sites : List = FunctionDefinitionNetwork.jsonEstIntDict["VPNs_Dict"]["vpn1"]["sitesIDs"] + sites : List = WriteNetworkFunctionDefinition.jsonEstIntDict["VPNs_Dict"]["vpn1"]["sitesIDs"] - site: str = FunctionDefinitionNetwork.jsonEstIntDict["VPNs_Dict"]["vpn1"]["sitesIDs"][numeroDeNode] - service_access: str = FunctionDefinitionNetwork.jsonEstIntDict["VPNs_Dict"]["vpn1"]["sites_Dict"][sites[numeroDeNode]]["site-network-accesses"]["site-network-accesses-IDs"][0] + site: str = WriteNetworkFunctionDefinition.jsonEstIntDict["VPNs_Dict"]["vpn1"]["sitesIDs"][numeroDeNode] + service_access: str = WriteNetworkFunctionDefinition.jsonEstIntDict["VPNs_Dict"]["vpn1"]["sites_Dict"][sites[numeroDeNode]]["site-network-accesses"]["site-network-accesses-IDs"][0] #con los siguientes ifs hago que las politicas cambien de lugar, uno si y uno no constantemente if numeroDeNode % 2 is 0: - arrayVpnNode.append(FunctionDefinitionNetwork.writeNode(FunctionDefinitionNetwork.jsonEstIntDict,site,service_access,numeroDeNode, 0)) + arrayVpnNode.append(WriteNetworkFunctionDefinition.writeNode(WriteNetworkFunctionDefinition.jsonEstIntDict,site,service_access,numeroDeNode, 0)) if numeroDeNode % 2 is not 0: - arrayVpnNode.append(FunctionDefinitionNetwork.writeNode(FunctionDefinitionNetwork.jsonEstIntDict,site,service_access,numeroDeNode, 1)) + arrayVpnNode.append(WriteNetworkFunctionDefinition.writeNode(WriteNetworkFunctionDefinition.jsonEstIntDict,site,service_access,numeroDeNode, 1)) jsonNet["vpn-service"]["vpn-nodes"]["vpn-node"] = arrayVpnNode #Codigo para crear el archivo y escribir el json en el - with open(FunctionDefinitionNetwork.output_file_path, 'w') as json_file: + with open(WriteNetworkFunctionDefinition.output_file_path, 'w') as json_file: json.dump(jsonNet, json_file, indent = 3) return jsonNet @@ -133,7 +135,7 @@ class FunctionDefinitionNetwork: } } vpnNetworkAccessElement.update({"status":status}) - vpnNetworkAccessElement.update({"connection":FunctionDefinitionNetwork.writeConnection()}) + vpnNetworkAccessElement.update({"connection":WriteNetworkFunctionDefinition.writeConnection()}) vpn_id : str = jsonEstDict["VPN_IDs"][0]#valido cuando solo hay una VPN. #de momento no IPv6, campo vacio, solo IPv4 diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/orignal_L3VPN_Services b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/orignal_L3VPN_Services new file mode 100644 index 0000000000000000000000000000000000000000..6bd57c8238c1af63ed3f504593f3c70cf8a68cc6 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/orignal_L3VPN_Services @@ -0,0 +1,80 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# 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. + +import logging +from typing import Dict, List +from flask import request +from flask.json import jsonify +from flask_restful import Resource +from werkzeug.exceptions import UnsupportedMediaType +from nbi.service.rest_server.nbi_plugins.tools.HttpStatusCodes import HTTP_CREATED, HTTP_SERVERERROR +from nbi.service.rest_server.nbi_plugins.tools.Authentication import HTTP_AUTH +from .Handlers import process_site, process_vpn_service +from .YangValidator import YangValidator + +LOGGER = logging.getLogger(__name__) + +class L3VPN_Services(Resource): + @HTTP_AUTH.login_required + def get(self): + return {} + + @HTTP_AUTH.login_required + def post(self): + if not request.is_json: raise UnsupportedMediaType('JSON payload is required') + request_data : Dict = request.json + LOGGER.debug('Request: {:s}'.format(str(request_data))) + + errors = list() + if 'ietf-l3vpn-svc:l3vpn-services' in request_data: + # processing multiple L3VPN service requests formatted as: + #{ + # "ietf-l3vpn-svc:l3vpn-services": { + # "l3vpn-svc": [ + # { + # "service-id": "vpn1", + # "vpn-services": { + # "vpn-service": [ + for l3vpn_svc in request_data['ietf-l3vpn-svc:l3vpn-services']['l3vpn-svc']: + l3vpn_svc.pop('service-id', None) + l3vpn_svc_request_data = {'ietf-l3vpn-svc:l3vpn-svc': l3vpn_svc} + errors.extend(self._process_l3vpn(l3vpn_svc_request_data)) + elif 'ietf-l3vpn-svc:l3vpn-svc' in request_data: + # processing single (standard) L3VPN service request formatted as: + #{ + # "ietf-l3vpn-svc:l3vpn-svc": { + # "vpn-services": { + # "vpn-service": [ + errors.extend(self._process_l3vpn(request_data)) + else: + errors.append('unexpected request: {:s}'.format(str(request_data))) + + response = jsonify(errors) + response.status_code = HTTP_CREATED if len(errors) == 0 else HTTP_SERVERERROR + return response + + def _process_l3vpn(self, request_data : Dict) -> List[Dict]: + yang_validator = YangValidator('ietf-l3vpn-svc') + request_data = yang_validator.parse_to_dict(request_data) + yang_validator.destroy() + + errors = list() + + for vpn_service in request_data['l3vpn-svc']['vpn-services']['vpn-service']: + process_vpn_service(vpn_service, errors) + + for site in request_data['l3vpn-svc']['sites']['site']: + process_site(site, errors) + + return errors