diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/FunctionDefinition.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/FunctionDefinition.py new file mode 100644 index 0000000000000000000000000000000000000000..2a42d2578efd7194a713804d8fe6a44bea3ec36a --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/FunctionDefinition.py @@ -0,0 +1,426 @@ +from typing import Dict, List + + +class FunctionDefinition: + + #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 = FunctionDefinition.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({"devices": FunctionDefinition.readDevices(siteElement.get('devices'))}) + res.update({"routing-protocol" : FunctionDefinition.readRoutingProtocols(siteElement.get('routing-protocols'))}) + res.update({"site-network-accesses" : FunctionDefinition.readSiteNetworkAccesses(siteElement.get('site-network-accesses'))}) + return res + #FALTAN MUCHOS CAMPOS A LEER EN site, como locations, 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)) + 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({"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":FunctionDefinition.readIpConnection(siteNetworkAccess.get("ip-connection"))}) + dictDeCadaID.update({"routing-protocols":FunctionDefinition.readRoutingProtocols(siteNetworkAccess.get("routing-protocols"))}) + dictDeCadaID.update({"service":FunctionDefinition.readService(siteNetworkAccess.get("service"))}) + # except NoJSONFieldException as e: + # print(e) + + dictSiteNetworkAccesses.update({site:dictDeCadaID}) + + 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": FunctionDefinition.readIpv4_Ipv6(ipConnection.get("ipv4"))}) + elif ipConnection.get("ipv6") is not None: + res.update({"ipv6":FunctionDefinition.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-input-bandwidth":service.get("svc-input-bandwidth")}) + res.update({"svc-output-bandwidth":service.get("svc-output-bandwidth")}) + res.update({"svc-mtu":service.get("svc-mtu")}) + qos : Dict = service.get("qos") + ruleArrayIni : List = qos.get("qos-classification-policy") + ruleArrayFin : List = [] + for elementRule in ruleArrayIni : + elementRuleFin : Dict = {} + elementRuleFin.update({"id":elementRule.get("id")}) + matchTypeIni : Dict = elementRule.get("match-type") + #CUIDADO, FALTAN MUCHOS CAMPOS DENTRO DE match-flow Y match-application POR PROGRAMAR + matchTypeFin : Dict = {} + if "match-flow" in matchTypeIni: + matchTypeFin.update({"match-flow":matchTypeIni}) + elif "match-application" in matchTypeIni: + matchTypeFin.update({"match-application" : matchTypeIni}) + #else: + #raise NoJSONFieldException("qos>qos-classification-policy>match-type") + + + elementRuleFin.update({"match-type":matchTypeFin}) + elementRuleFin.update({"target-class-id":elementRule.get("target-class-id")}) + ruleArrayFin.append(elementRuleFin) + res.update({"qos-classification-policy":ruleArrayFin}) + + #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/FunctionDefinitionNetwork.py new file mode 100644 index 0000000000000000000000000000000000000000..940fd00c9c1489e2a7d1446b3c32cf4c7d2db10b --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/FunctionDefinitionNetwork.py @@ -0,0 +1,286 @@ +from typing import Dict, List +import json + +class FunctionDefinitionNetwork: + + jsonEstructuraIntermedia = open("/home/ubuntu/Documents/EstructuraIntermedia.json", "r") + output_file_path = "/home/ubuntu/Documents/L3VPN_ntw.json" + jsonEstructuraIntermediaData : str = jsonEstructuraIntermedia.read() + jsonEstIntDict : Dict = json.loads(jsonEstructuraIntermediaData) + + @staticmethod + def numberVpnIDs() -> int : + vpnIDs : List = FunctionDefinitionNetwork.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"] + return len(sitesIDs) + + @staticmethod + def write() -> Dict: + + 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 + jsonNet["vpn-service"]["vpn-id"] = vpn_id #de momento solo 1 vpn + # jsonNet["vpn-service"]["customer-name"] = "_" + # jsonNet["vpn-service"]["parent-service-id"] = "_" + jsonNet["vpn-service"]["vpn-type"] = "L3VRF" + jsonNet["vpn-service"]["vpn-service-topology"] = "custom" #modificar todos estos campos + jsonNet["vpn-service"]["vpn-nodes"] = {} + + arrayVpnNode : List = [] + print(FunctionDefinitionNetwork.numberSitesIDS()) + for numeroDeNode in range(FunctionDefinitionNetwork.numberSitesIDS()): + + sites : List = FunctionDefinitionNetwork.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] + + #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)) + if numeroDeNode % 2 is not 0: + arrayVpnNode.append(FunctionDefinitionNetwork.writeNode(FunctionDefinitionNetwork.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: + json.dump(jsonNet, json_file, indent = 3) + + return jsonNet + + def writeNode(jsonEstDict : Dict, site: str, service_access : str, numeroDeNode : int, importExport : int) -> Dict: + res: Dict = {} + res.update({"ne-id": "pe" + str(numeroDeNode+1)}) + res.update({"vpn-node-id": "PE" + str(numeroDeNode+1)}) + res.update({"local-as":"65000"}) + res.update({"router-id":"5.5.5.5"}) + + #active-vpn-instance-profiles + activeVpnInstanceProfiles: Dict = {} + vpnInstanceProfilesArray: List = [] + vpnInstanceProfilesArrayObject: Dict = {} + + vpnInstanceProfilesArrayObject.update({"profile-id": "1"}) + routerID : Dict = {} + + #Pregutnar sobre si route distinguisher debe cambiar por cada node + directlyAsigned : Dict = {"rd": str(res.get("local-as")) + ":100"} + routerID.update({"directly-assigned":directlyAsigned}) + vpnInstanceProfilesArrayObject.update({"router-id":routerID}) + + addressFamily : List = [] + addressFamilyElement : Dict = {} + addressFamilyElement.update({"address-family":""}) + + vpnTargets: Dict = {} + vpnTarget: List = [] + vpnTargetObject: Dict = {} + vpnTargetObject.update({"id": str(numeroDeNode+1)}) + + routeTargets: List = [] + routeTargetsElement: Dict = {} + routeTargetsElement.update({"route-target": str(res.get("local-as"))+":222"}) #Any to any + routeTargets.append(routeTargetsElement) + + vpnTargetObject.update({"route-targets": routeTargets}) + vpnTargetObject.update({"route-target-type": "both"}) #Any to any + + vpnTarget.append(vpnTargetObject) + vpnTargets.update({"vpn-target": vpnTarget}) + + vpnPolicies: Dict = {} + #Codigo que en metodo write() hace que un nodo si y uno no vayan modificando el orden de politicas + if importExport is 0: + vpnPolicies.update({"import-policy":"politica 1"}) + vpnPolicies.update({"export-policy":"politica 2"}) + else: + vpnPolicies.update({"import-policy":"politica 2"}) + vpnPolicies.update({"export-policy":"politica 1"}) + vpnTargets.update({"vpn-policies":vpnPolicies}) + + addressFamilyElement.update({"vpn-targets":vpnTargets}) + addressFamily.append(addressFamilyElement) + + vpnInstanceProfilesArrayObject.update({"address-family": addressFamily}) + vpnInstanceProfilesArray.append(vpnInstanceProfilesArrayObject) + activeVpnInstanceProfiles.update({"vpn-instance-profile": vpnInstanceProfilesArray}) + res.update({"active-vpn-instance-profiles": activeVpnInstanceProfiles}) + + #vpn-network-accesses + vpnNetworkAccesses : Dict = {} + vpnNetworkAccessArray : List = [] + vpnNetworkAccessElement : Dict = {} + vpnNetworkAccessElement.update({"id":"1-1-1"}) + vpnNetworkAccessElement.update({"interface-id":"1-1-1"}) + vpnNetworkAccessElement.update({"vpn-network-access-type":"l3ipvlan"}) + vpnNetworkAccessElement.update({"vpn-instance-profiles":""}) + + status : Dict = { + "admin-status":{ + "status":"admin-up", + "last-change":"" + }, + "oper-status":{ + "status":"", + "last-change":"" + } + } + vpnNetworkAccessElement.update({"status":status}) + vpnNetworkAccessElement.update({"connection":FunctionDefinitionNetwork.writeConnection()}) + + vpn_id : str = jsonEstDict["VPN_IDs"][0]#valido cuando solo hay una VPN. + #de momento no IPv6, campo vacio, solo IPv4 + provider_address: str = jsonEstDict["VPNs_Dict"][vpn_id]["sites_Dict"][site]["site-network-accesses"]["site-network-accesses-Dict"][service_access]["ip-connection"]["ipv4"]["addresses"]["provider-address"] + prefixe_length : int = jsonEstDict["VPNs_Dict"][vpn_id]["sites_Dict"][site]["site-network-accesses"]["site-network-accesses-Dict"][service_access]["ip-connection"]["ipv4"]["addresses"]["prefixe-length"] + customer_address : str = jsonEstDict["VPNs_Dict"][vpn_id]["sites_Dict"][site]["site-network-accesses"]["site-network-accesses-Dict"][service_access]["ip-connection"]["ipv4"]["addresses"]["customer-address"] + + ipConnectionAndRoutingProtocols : Dict = { + "ip-connection":{ + "l3-termination-point":"1-1-1.222", + "ipv4":{ + "local-address":provider_address, + "prefixe-length":prefixe_length, + "address-allocation-type":"static-address", + "static-addresses":{ + "primary-addresses":"1", + "address":[ + { + "address-id":"1", + "customer-address":customer_address + } + ] + } + }, + "ipv6":{ + "local-address":"", + "prefixe-lenght":"", + "address-allocation-type":"static-address", + "static-addresses":{ + "primary-addresses":"1", + "address":[ + { + "address-id":"1", + "customer-address":"" + } + ] + } + } + }, + "routing-protocols":{ + "routing-protocol":[ + { + "id":"ver con oscar", + "type":"static-routing", + "routing-profiles":[ + { + "id":"", + "type":"" + + } + ], + "static":{ + "cascaded-lan-prefixes":{ + "ipv4-lan-prefixes":[ + { + "lan":"", + "next-hop":"", + "lan-tag":"", + "bfd-enable":"", + "metric":"", + "preference":"", + "status":{ + "admin-status":{ + "status":"", + "last-change":"" + }, + "oper-status":{ + "status":"", + "last-change":"" + } + }, + "tef-l3vpn-ntw:description":"" + } + ], + "ipv6-lan-prefixes":[ + { + "lan":"", + "next-hop":"", + "lan-tag":"", + "bfd-enable":"", + "metric":"", + "preference":"", + "status":{ + "admin-status":{ + "status":"", + "last-change":"" + }, + "oper-status":{ + "status":"", + "last-change":"" + } + }, + "tef-l3vpn-ntw:description":"" + } + ] + } + } + } + ] + } + } + + vpnNetworkAccessElement.update(ipConnectionAndRoutingProtocols) + + vpnNetworkAccessArray.append(vpnNetworkAccessElement) + vpnNetworkAccesses.update({"vpn-network-access":vpnNetworkAccessArray}) + res.update({"vpn-network-accesses":vpnNetworkAccesses}) + return res + + + #Falta "l2vpn-id":"", que es la otra choice entre l2vpn-id y l2-tunnel-service + def writeConnection() -> Dict: + res : Dict = { + "encapsulation":{ + "type":"untagged" + # , + # "dot1q":{ + # "tag-type":"cvlan", + # "cvlan-id":"222" + # } + # , + # "priotity-tagged":{ + # "tag-type":"" + # }, + # "quinq":{ + # "tag-type":"", + # "svlan-id":"", + # "cvlan-id":"" + # } + } + # , + # "l2-tunnel-service":{ + # "type":"", + # "pseudowire":{ + # "vcid":"", + # "far-end":"" + # }, + # "vpls":{ + # "vcid":"", + # "far-end":"" + # }, + # "vxlan":{ + # "vni-id":"", + # "peer-mode":"", + # "peer-ip-address":"" + # } + # } + } + + return res \ No newline at end of file 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 13d5c532478e93ef1bd3b1c644d7f3c3ba927af5..47be3ee5622d882279a0ca7d295bfdfd7d302081 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 @@ -13,7 +13,9 @@ # limitations under the License. import logging -from typing import Dict +import json + +from typing import Dict, List from flask import request from flask.json import jsonify from flask_restful import Resource @@ -23,6 +25,11 @@ 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): @@ -32,22 +39,64 @@ 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(request_data))) + LOGGER.debug('Request: {:s}'.format(str(jsonAValidarDict))) yang_validator = YangValidator('ietf-l3vpn-svc') - request_data = yang_validator.parse_to_dict(request_data) + request_data = yang_validator.parse_to_dict(jsonAValidarDict) yang_validator.destroy() - errors = [] + #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 = [] - 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 + # response = jsonify(errors) + # response.status_code = HTTP_CREATED if len(errors) == 0 else HTTP_SERVERERROR + # return response + \ No newline at end of file diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Main.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Main.py new file mode 100644 index 0000000000000000000000000000000000000000..d215a73ae373420011e18f95f2d02fe4b5b47ddd --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Main.py @@ -0,0 +1,6 @@ +from src.nbi.service.rest_server.nbi_plugins.ietf_l3vpn import L3VPN_Services +#from src.nbi.service.rest_server.nbi_plugins.ietf_l3vpn.L3VPN_Services import L3VPN_Services + + +service : L3VPN_Services = L3VPN_Services() +service.post() diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/run_tests_locally-nbi-ietf-l3vpn.sh b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/run_tests_locally-nbi-ietf-l3vpn.sh new file mode 100755 index 0000000000000000000000000000000000000000..0bd133e16caac438c8da1a5795fce89e99230c9a --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/run_tests_locally-nbi-ietf-l3vpn.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# 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. + + +PROJECTDIR=`pwd` + +cd $PROJECTDIR/src +RCFILE=$PROJECTDIR/coverage/.coveragerc + +# Run unitary tests and analyze coverage of code at same time +# helpful pytest flags: --log-level=INFO -o log_cli=true --verbose --maxfail=1 --durations=0 +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + nbi/tests/test_ietf_l3vpn.py diff --git a/src/nbi/tests/test_ietf_l3vpn.py b/src/nbi/tests/test_ietf_l3vpn.py index da9efdffd4dd6f4b3af956988aa3d51d3e855c84..64f72514fab449a1189b847cd0bf64914e97d0b7 100644 --- a/src/nbi/tests/test_ietf_l3vpn.py +++ b/src/nbi/tests/test_ietf_l3vpn.py @@ -12,7 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json, logging, pytest +import json, logging +import pytest from typing import Dict from common.Constants import DEFAULT_CONTEXT_NAME from common.proto.context_pb2 import ContextId