diff --git a/my_deploy.sh b/my_deploy.sh index 10f15bbbddc775273a72eb7b739bc6d9a8786d5e..f1b4b4f92f5370bc144e8aa4a48b2eee26f9c6b0 100755 --- a/my_deploy.sh +++ b/my_deploy.sh @@ -20,7 +20,7 @@ export TFS_REGISTRY_IMAGES="http://localhost:32000/tfs/" # Set the list of components, separated by spaces, you want to build images for, and deploy. -export TFS_COMPONENTS="alto context device pathcomp service slice nbi webui load_generator" +export TFS_COMPONENTS=" context device pathcomp service slice nbi webui load_generator" # Uncomment to activate Monitoring #export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring" diff --git a/proto/context.proto b/proto/context.proto index 9cfe8fe07c87926530f8973c964d68dd0d3fac83..40f54b13588ec40b244a5cfed0171069796faa59 100644 --- a/proto/context.proto +++ b/proto/context.proto @@ -16,6 +16,7 @@ syntax = "proto3"; package context; import "acl.proto"; +import "ip_link.proto"; import "kpi_sample_types.proto"; service ContextService { @@ -513,9 +514,9 @@ message ConfigRule_ACL { acl.AclRuleSet rule_set = 2; } -message ConfigRule_IP_LIK { - EndPointId endpoint_id = 1; - string subnet_ip = 2; +message ConfigRule_IP_LINK { + EndPointId endpoint_id = 1; + ip_link.IpLinkRuleSet rule_set = 2; } message ConfigRule { @@ -523,7 +524,7 @@ message ConfigRule { oneof config_rule { ConfigRule_Custom custom = 2; ConfigRule_ACL acl = 3; - ConfigRule_IP_LIK ip_link = 4; + ConfigRule_IP_LINK ip_link = 4; } } diff --git a/proto/ip_link.proto b/proto/ip_link.proto new file mode 100644 index 0000000000000000000000000000000000000000..79a5bed5adbc749ba3e200b44d2cafea6bac7615 --- /dev/null +++ b/proto/ip_link.proto @@ -0,0 +1,24 @@ +// Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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. + +syntax = "proto3"; +package ip_link; + + + +message IpLinkRuleSet { + string ip = 1; + string mask = 3; + string vlan = 4; +} diff --git a/src/common/tools/object_factory/Service.py b/src/common/tools/object_factory/Service.py index 32b99a31f22072874ab894de2a87ce2b7d56ba85..642aa5a032b88bc10eea99cb174f76d5b2884d0f 100644 --- a/src/common/tools/object_factory/Service.py +++ b/src/common/tools/object_factory/Service.py @@ -80,4 +80,14 @@ def json_service_p4_planned( return json_service( service_uuid, ServiceTypeEnum.SERVICETYPE_L2NM, context_id=json_context_id(context_uuid), status=ServiceStatusEnum.SERVICESTATUS_PLANNED, endpoint_ids=endpoint_ids, constraints=constraints, + config_rules=config_rules) + +def json_service_iplink_planned( + service_uuid : str, endpoint_ids : List[Dict] = [], constraints : List[Dict] = [], + config_rules : List[Dict] = [], context_uuid : str = DEFAULT_CONTEXT_NAME + ): + + return json_service( + service_uuid, ServiceTypeEnum.SERVICETYPE_IPLINK, context_id=json_context_id(context_uuid), + status=ServiceStatusEnum.SERVICESTATUS_PLANNED, endpoint_ids=endpoint_ids, constraints=constraints, config_rules=config_rules) \ No newline at end of file diff --git a/src/common/type_checkers/Assertions.py b/src/common/type_checkers/Assertions.py index 90b7d976b0f6fff9d478ce7b40188240a8eea2d6..be4ae7a635a5e42cbb829ba9cbd8c492f01e3deb 100644 --- a/src/common/type_checkers/Assertions.py +++ b/src/common/type_checkers/Assertions.py @@ -116,6 +116,7 @@ def validate_uuid(message, allow_empty=False): CONFIG_RULE_TYPES = { 'custom', 'acl', + 'ip_link' } def validate_config_rule(message): assert isinstance(message, dict) diff --git a/src/context/service/database/ConfigRule.py b/src/context/service/database/ConfigRule.py index 0f204c505005fdfe84755c3d179f3cea56c75355..e2b19420a617617cf1d4369befce20b9d45ff842 100644 --- a/src/context/service/database/ConfigRule.py +++ b/src/context/service/database/ConfigRule.py @@ -68,7 +68,7 @@ def compose_config_rules_data( _, _, endpoint_uuid = endpoint_get_uuid(config_rule.acl.endpoint_id, allow_random=False) rule_set_name = config_rule.acl.rule_set.name configrule_name = '{:s}:{:s}:{:s}:{:s}'.format(parent_kind, kind.value, endpoint_uuid, rule_set_name) - elif kind == ConfigRuleKindEnum.IP_LINK: + elif kind == ConfigRuleKindEnum.IP_LINK: _, _, endpoint_uuid = endpoint_get_uuid(config_rule.ip_link.endpoint_id, allow_random=False) configrule_name = '{:s}:{:s}:{:s}'.format(parent_kind, kind.value, endpoint_uuid) else: diff --git a/src/context/service/database/models/ConfigRuleModel.py b/src/context/service/database/models/ConfigRuleModel.py index 73a667e6bc1d19a8182ff3f836e872c42766728d..5799934a76c0240535984ecf297b0baf54426450 100644 --- a/src/context/service/database/models/ConfigRuleModel.py +++ b/src/context/service/database/models/ConfigRuleModel.py @@ -23,7 +23,7 @@ from ._Base import _Base class ConfigRuleKindEnum(enum.Enum): CUSTOM = 'custom' ACL = 'acl' - IP_LINK = 'ip_link' + IP_LINK = 'ip_link' class DeviceConfigRuleModel(_Base): __tablename__ = 'device_configrule' diff --git a/src/context/service/database/models/enums/ServiceType.py b/src/context/service/database/models/enums/ServiceType.py index 62d5380b56803b3cc21dd1456292ec9df470cb15..bcc5aff09da2899470d6c56e82cad5a6d0f00f95 100644 --- a/src/context/service/database/models/enums/ServiceType.py +++ b/src/context/service/database/models/enums/ServiceType.py @@ -29,6 +29,7 @@ class ORM_ServiceTypeEnum(enum.Enum): TE = ServiceTypeEnum.SERVICETYPE_TE E2E = ServiceTypeEnum.SERVICETYPE_E2E OPTICAL_CONNECTIVITY = ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY + IP_LINK = ServiceTypeEnum.SERVICETYPE_IPLINK grpc_to_enum__service_type = functools.partial( grpc_to_enum, ServiceTypeEnum, ORM_ServiceTypeEnum) diff --git a/src/device/service/Tools.py b/src/device/service/Tools.py index 91926b9e59cccac2e233ac14bbac497bbb0ac15c..8eb25578488dd0fe5b1781a0e129709b86df69fa 100644 --- a/src/device/service/Tools.py +++ b/src/device/service/Tools.py @@ -306,7 +306,13 @@ def compute_rules_to_add_delete( ACL_KEY_TEMPLATE = '/device[{:s}]/endpoint[{:s}]/acl_ruleset[{:s}]' key_or_path = ACL_KEY_TEMPLATE.format(device_uuid, endpoint_uuid, acl_ruleset_name) context_config_rules[key_or_path] = grpc_message_to_json(config_rule.acl) # get the resource value of the acl - + elif config_rule_kind == 'ip_link': + device_uuid = config_rule.ip_link.endpoint_id.device_id.device_uuid.uuid # get the device name + endpoint_uuid = config_rule.ip_link.endpoint_id.endpoint_uuid.uuid # get the endpoint name request_config_rules = [] + ip_link_ruleset_name = config_rule.ip_link.rule_set.name # get the ip_link name + IP_LINK_KEY_TEMPLATE = '/device[{:s}]/endpoint[{:s}]/ip_link_ruleset[{:s}]' + key_or_path = IP_LINK_KEY_TEMPLATE.format(device_uuid, endpoint_uuid, ip_link_ruleset_name) + context_config_rules[key_or_path] = grpc_message_to_json(config_rule.ip_link) # get the resource value of the ip_link request_config_rules = [] for config_rule in request.device_config.config_rules: config_rule_kind = config_rule.WhichOneof('config_rule') @@ -323,6 +329,15 @@ def compute_rules_to_add_delete( request_config_rules.append(( config_rule.action, key_or_path, grpc_message_to_json(config_rule.acl) )) + elif config_rule_kind == 'ip_link': # resource management of "ip_link" rule + device_uuid = config_rule.ip_link.endpoint_id.device_id.device_uuid.uuid + endpoint_uuid = config_rule.ip_link.endpoint_id.endpoint_uuid.uuid + ip_link_ruleset_name = config_rule.ip_link.rule_set.name + IP_LINK_KEY_TEMPLATE = '/device[{:s}]/endpoint[{:s}]/ip_link_ruleset[{:s}]' + key_or_path = IP_LINK_KEY_TEMPLATE.format(device_uuid, endpoint_uuid, ip_link_ruleset_name) + request_config_rules.append(( + config_rule.action, key_or_path, grpc_message_to_json(config_rule.ip_link) + )) resources_to_set : List[Tuple[str, Any]] = [] # key, value resources_to_delete : List[Tuple[str, Any]] = [] # key, value 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 deleted file mode 100644 index e57011be614cc6af8bc011b4fd8581e86f0856c5..0000000000000000000000000000000000000000 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/EstructuraIntermedia.json +++ /dev/null @@ -1,231 +0,0 @@ -{ - "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/FunctionDefinition.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/FunctionDefinition.py deleted file mode 100644 index 2a42d2578efd7194a713804d8fe6a44bea3ec36a..0000000000000000000000000000000000000000 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/FunctionDefinition.py +++ /dev/null @@ -1,426 +0,0 @@ -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/Handlers.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py index 1a87ec910262df5671fd681c4205c13b4a5678f6..f7329cb35666f423e85f99510e5f89a82e89b7f8 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, json +import logging, netaddr from typing import Dict, List, Optional, Tuple from common.Constants import DEFAULT_CONTEXT_NAME from common.proto.context_pb2 import Service, ServiceStatusEnum, ServiceTypeEnum @@ -26,9 +26,6 @@ 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( @@ -212,55 +209,3 @@ 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/Main.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Main.py deleted file mode 100644 index d215a73ae373420011e18f95f2d02fe4b5b47ddd..0000000000000000000000000000000000000000 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Main.py +++ /dev/null @@ -1,6 +0,0 @@ -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/ReadServiceFunctionDefinition.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/ReadServiceFunctionDefinition.py deleted file mode 100644 index 3218a18ddd2d5205c170723b2adaaadf323caddd..0000000000000000000000000000000000000000 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/ReadServiceFunctionDefinition.py +++ /dev/null @@ -1,418 +0,0 @@ -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/WriteNetworkFunctionDefinition.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/WriteNetworkFunctionDefinition.py deleted file mode 100644 index 5c22de56dee9140467fb747c4d43db2a3ab25bcc..0000000000000000000000000000000000000000 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/WriteNetworkFunctionDefinition.py +++ /dev/null @@ -1,288 +0,0 @@ -from typing import Dict, List -import json, os - -class WriteNetworkFunctionDefinition: - - 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 = 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 = WriteNetworkFunctionDefinition.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 = 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"] = "_" - 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(WriteNetworkFunctionDefinition.numberSitesIDS()) - for numeroDeNode in range(WriteNetworkFunctionDefinition.numberSitesIDS()): - - sites : List = WriteNetworkFunctionDefinition.jsonEstIntDict["VPNs_Dict"]["vpn1"]["sitesIDs"] - - 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(WriteNetworkFunctionDefinition.writeNode(WriteNetworkFunctionDefinition.jsonEstIntDict,site,service_access,numeroDeNode, 0)) - if numeroDeNode % 2 is not 0: - 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(WriteNetworkFunctionDefinition.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":WriteNetworkFunctionDefinition.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/orignal_L3VPN_Services b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/orignal_L3VPN_Services deleted file mode 100644 index 6bd57c8238c1af63ed3f504593f3c70cf8a68cc6..0000000000000000000000000000000000000000 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/orignal_L3VPN_Services +++ /dev/null @@ -1,80 +0,0 @@ -# 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 diff --git a/src/pathcomp/frontend/service/algorithms/_Algorithm.py b/src/pathcomp/frontend/service/algorithms/_Algorithm.py index 3ed2b13fb33ae06faeacc4286959a8016ca995d1..d4e4194497be44bf9eb0176bcc9014e9402e582c 100644 --- a/src/pathcomp/frontend/service/algorithms/_Algorithm.py +++ b/src/pathcomp/frontend/service/algorithms/_Algorithm.py @@ -23,7 +23,7 @@ from pathcomp.frontend.Config import BACKEND_URL from .tools.EroPathToHops import eropath_to_hops from .tools.ComposeConfigRules import ( compose_device_config_rules, compose_l2nm_config_rules, compose_l3nm_config_rules, compose_tapi_config_rules, - generate_neighbor_endpoint_config_rules + generate_neighbor_endpoint_config_rules, compose_iplink_config_rules ) from .tools.ComposeRequest import compose_device, compose_link, compose_service from .tools.ComputeSubServices import ( @@ -182,6 +182,8 @@ class _Algorithm: compose_l3nm_config_rules(config_rules, service.service_config.config_rules) elif service_type == ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE: compose_tapi_config_rules(config_rules, service.service_config.config_rules) + if service_type == ServiceTypeEnum.SERVICETYPE_IPLINK: + compose_iplink_config_rules(config_rules, service.service_config.config_rules) else: MSG = 'Unhandled generic Config Rules for service {:s} {:s}' self.logger.warning(MSG.format(str(service_uuid), str(ServiceTypeEnum.Name(service_type)))) diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py index f92f9b2fff11ab585813ab59e07c463f361413d2..823741a0f49aea74604d9bfd5c3f909e7611bb8c 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py @@ -103,7 +103,12 @@ def compose_tapi_config_rules(main_service_config_rules : List, subservice_confi ] for rule_name, defaults in CONFIG_RULES: compose_config_rules(main_service_config_rules, subservice_config_rules, rule_name, defaults) - + +def compose_iplink_config_rules(main_service_config_rules : List, subservice_config_rules : List) -> None: + CONFIG_RULES = [SETTINGS_RULE_NAME] + for rule_name, defaults in CONFIG_RULES: + compose_config_rules(main_service_config_rules, subservice_config_rules, rule_name, defaults) + def compose_device_config_rules( config_rules : List, subservice_config_rules : List, path_hops : List, device_name_mapping : Dict[str, str], endpoint_name_mapping : Dict[Tuple[str, str], str] @@ -152,6 +157,31 @@ def compose_device_config_rules( LOGGER.debug('[compose_device_config_rules] adding acl config rule') subservice_config_rules.append(config_rule) + + elif config_rule.WhichOneof('config_rule') == 'ip_link': + LOGGER.debug('[compose_device_config_rules] is ip_link') + endpoint_id = config_rule.ip_link.endpoint_id + device_uuid_or_name = endpoint_id.device_id.device_uuid.uuid + LOGGER.debug('[compose_device_config_rules] device_uuid_or_name={:s}'.format(str(device_uuid_or_name))) + device_name_or_uuid = device_name_mapping.get(device_uuid_or_name, device_uuid_or_name) + LOGGER.debug('[compose_device_config_rules] device_name_or_uuid={:s}'.format(str(device_name_or_uuid))) + device_keys = {device_uuid_or_name, device_name_or_uuid} + if len(device_keys.intersection(devices_traversed)) == 0: continue + + endpoint_uuid = endpoint_id.endpoint_uuid.uuid + LOGGER.debug('[compose_device_config_rules] endpoint_uuid={:s}'.format(str(endpoint_uuid))) + # given endpoint uuids link 'eth-1/0/20.533', remove last part after the '.' + endpoint_uuid_or_name = (endpoint_uuid[::-1].split('.', maxsplit=1)[-1])[::-1] + LOGGER.debug('[compose_device_config_rules] endpoint_uuid_or_name={:s}'.format(str(endpoint_uuid_or_name))) + endpoint_name_or_uuid_1 = endpoint_name_mapping[(device_uuid_or_name, endpoint_uuid_or_name)] + endpoint_name_or_uuid_2 = endpoint_name_mapping[(device_name_or_uuid, endpoint_uuid_or_name)] + endpoint_keys = {endpoint_uuid_or_name, endpoint_name_or_uuid_1, endpoint_name_or_uuid_2} + + device_endpoint_keys = set(itertools.product(device_keys, endpoint_keys)) + if len(device_endpoint_keys.intersection(endpoints_traversed)) == 0: continue + + LOGGER.debug('[compose_device_config_rules] adding ip_link config rule') + subservice_config_rules.append(config_rule) elif config_rule.WhichOneof('config_rule') == 'custom': LOGGER.debug('[compose_device_config_rules] is custom') diff --git a/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py b/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py index 2792a86639fbd6852a41499e928de7a4131ed408..07d874b939b572209e7eba80748eca4ad723cc49 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py @@ -45,6 +45,8 @@ SERVICE_TYPE_L2NM = {ServiceTypeEnum.SERVICETYPE_L2NM} SERVICE_TYPE_L3NM = {ServiceTypeEnum.SERVICETYPE_L3NM} SERVICE_TYPE_LXNM = {ServiceTypeEnum.SERVICETYPE_L3NM, ServiceTypeEnum.SERVICETYPE_L2NM} SERVICE_TYPE_TAPI = {ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE} +SERVICE_TYPE_IPLINK = {ServiceTypeEnum.SERVICETYPE_IPLINK} + def get_service_type(device_type : DeviceTypeEnum, prv_service_type : ServiceTypeEnum) -> ServiceTypeEnum: if device_type in PACKET_DEVICE_TYPES and prv_service_type in SERVICE_TYPE_LXNM: return prv_service_type diff --git a/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java b/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java index 4593770498216267b8d2f95dd728fccfbb9dc134..b0742e306183a7d5da1a03a54d7734b1e398d75c 100644 --- a/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java +++ b/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java @@ -504,6 +504,10 @@ public final class ContextOuterClass { * <code>SERVICETYPE_OPTICAL_CONNECTIVITY = 6;</code> */ SERVICETYPE_OPTICAL_CONNECTIVITY(6), + /** + * <code>SERVICETYPE_IPLINK = 6;</code> + */ + SERVICETYPE_IPLINK(7), UNRECOGNIZED(-1); /** @@ -541,6 +545,11 @@ public final class ContextOuterClass { */ public static final int SERVICETYPE_OPTICAL_CONNECTIVITY_VALUE = 6; + /** + * <code>SERVICETYPE_IPLINK = 7;</code> + */ + public static final int SERVICETYPE_IPLINK_VALUE = 7; + public final int getNumber() { if (this == UNRECOGNIZED) { throw new java.lang.IllegalArgumentException("Can't get the number of an unknown enum value."); @@ -578,6 +587,8 @@ public final class ContextOuterClass { return SERVICETYPE_E2E; case 6: return SERVICETYPE_OPTICAL_CONNECTIVITY; + case 7: + return SERVICETYPE_IPLINK; default: return null; } diff --git a/src/service/service/service_handler_api/FilterFields.py b/src/service/service/service_handler_api/FilterFields.py index ca70fa9386e356e7e49397365701013e1d3a1697..e0811c0a9c53cb14fbd654797faf5f5e0522c4ac 100644 --- a/src/service/service/service_handler_api/FilterFields.py +++ b/src/service/service/service_handler_api/FilterFields.py @@ -26,7 +26,8 @@ SERVICE_TYPE_VALUES = { ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, ServiceTypeEnum.SERVICETYPE_TE, ServiceTypeEnum.SERVICETYPE_E2E, - ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY + ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY, + ServiceTypeEnum.SERVICETYPE_IPLINK } DEVICE_DRIVER_VALUES = { diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py index 8b5e2b2834f37dc3d616907e46cf1fa5b2f1274f..a731e8176d51b32305bb13bd223e19bfb44c2b6e 100644 --- a/src/service/service/service_handlers/__init__.py +++ b/src/service/service/service_handlers/__init__.py @@ -27,6 +27,7 @@ from .tapi_tapi.TapiServiceHandler import TapiServiceHandler from .tapi_xr.TapiXrServiceHandler import TapiXrServiceHandler from .e2e_orch.E2EOrchestratorServiceHandler import E2EOrchestratorServiceHandler from .oc.OCServiceHandler import OCServiceHandler +from .ip_link.IP_LinkServiceHandler import IP_LinkServiceHandler SERVICE_HANDLERS = [ (L2NMEmulatedServiceHandler, [ @@ -106,5 +107,11 @@ SERVICE_HANDLERS = [ FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY, FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_OC, } - ]) + ]), + (IP_LinkServiceHandler, [ + { + FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_IPLINK, + FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG, + } + ]), ] diff --git a/src/service/service/service_handlers/ip_link/ConfigRules.py b/src/service/service/service_handlers/ip_link/ConfigRules.py new file mode 100644 index 0000000000000000000000000000000000000000..74becce014cecbcd8f06550e420a68274318fa1f --- /dev/null +++ b/src/service/service/service_handlers/ip_link/ConfigRules.py @@ -0,0 +1,296 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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. + +from typing import Any, Dict, List, Optional, Tuple +from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set +from service.service.service_handler_api.AnyTreeTools import TreeNode + +def get_value(field_name : str, *containers, default=None) -> Optional[Any]: + if len(containers) == 0: raise Exception('No containers specified') + for container in containers: + if field_name not in container: continue + return container[field_name] + return default + +def setup_config_rules( + service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, endpoint_name : str, + service_settings : TreeNode, device_settings : TreeNode, endpoint_settings : TreeNode, endpoint_acls : List [Tuple], endpoint_ip_link : List [Tuple] +) -> List[Dict]: + + if service_settings is None: return [] + if device_settings is None: return [] + if endpoint_settings is None: return [] + + json_settings : Dict = service_settings.value + json_device_settings : Dict = device_settings.value + json_endpoint_settings : Dict = endpoint_settings.value + + settings = (json_settings, json_endpoint_settings, json_device_settings) + + mtu = get_value('mtu', *settings, default=1450) # 1512 + #address_families = json_settings.get('address_families', [] ) # ['IPV4'] + bgp_as = get_value('bgp_as', *settings, default=65000) # 65000 + + router_id = json_endpoint_settings.get('router_id', '0.0.0.0') # '10.95.0.10' + route_distinguisher = json_settings.get('route_distinguisher', '65000:101' ) # '60001:801' + sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1 + vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400 + address_ip = json_endpoint_settings.get('address_ip', '0.0.0.0') # '2.2.2.1' + address_prefix = json_endpoint_settings.get('address_prefix', 24 ) # 30 + + policy_import = json_endpoint_settings.get('policy_AZ', '2' ) # 2 + policy_export = json_endpoint_settings.get('policy_ZA', '7' ) # 30 + #network_interface_desc = '{:s}-NetIf'.format(service_uuid) + network_interface_desc = json_endpoint_settings.get('ni_description','') + #network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid) + network_subinterface_desc = json_endpoint_settings.get('subif_description','') + #service_short_uuid = service_uuid.split('-')[-1] + #network_instance_name = '{:s}-NetInst'.format(service_short_uuid) + network_instance_name = json_endpoint_settings.get('ni_name', service_uuid.split('-')[-1]) #ELAN-AC:1 + + if_subif_name = '{:s}.{:d}'.format(endpoint_name, vlan_id) + + json_config_rules = [ + # Configure Interface (not used) + #json_config_rule_set( + # '/interface[{:s}]'.format(endpoint_name), { + # 'name': endpoint_name, + # 'description': network_interface_desc, + # 'mtu': mtu, + #}), + + #Create network instance + json_config_rule_set( + '/network_instance[{:s}]'.format(network_instance_name), { + 'name': network_instance_name, + 'description': network_interface_desc, + 'type': 'L3VRF', + 'route_distinguisher': route_distinguisher, + #'router_id': router_id, + #'address_families': address_families, + }), + + #Add BGP protocol to network instance + json_config_rule_set( + '/network_instance[{:s}]/protocols[BGP]'.format(network_instance_name), { + 'name': network_instance_name, + 'protocol_name': 'BGP', + 'identifier': 'BGP', + 'type': 'L3VRF', + 'as': bgp_as, + 'router_id': router_id, + }), + + #Add DIRECTLY CONNECTED protocol to network instance + json_config_rule_set( + '/network_instance[{:s}]/protocols[DIRECTLY_CONNECTED]'.format(network_instance_name), { + 'name': network_instance_name, + 'identifier': 'DIRECTLY_CONNECTED', + 'protocol_name': 'DIRECTLY_CONNECTED', + }), + + #Add STATIC protocol to network instance + json_config_rule_set( + '/network_instance[{:s}]/protocols[STATIC]'.format(network_instance_name), { + 'name': network_instance_name, + 'identifier': 'STATIC', + 'protocol_name': 'STATIC', + }), + + #Create interface with subinterface + json_config_rule_set( + '/interface[{:s}]/subinterface[{:d}]'.format(if_subif_name, sub_interface_index), { + 'name' : if_subif_name, + 'type' :'l3ipvlan', + 'mtu' : mtu, + 'index' : sub_interface_index, + 'description' : network_subinterface_desc, + 'vlan_id' : vlan_id, + 'address_ip' : address_ip, + 'address_prefix': address_prefix, + }), + + #Associate interface to network instance + json_config_rule_set( + '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_subif_name), { + 'name' : network_instance_name, + 'type' : 'L3VRF', + 'id' : if_subif_name, + 'interface' : if_subif_name, + 'subinterface': sub_interface_index, + }), + + #Create routing policy + json_config_rule_set( + '/routing_policy/bgp_defined_set[{:s}_rt_import][{:s}]'.format(policy_import,route_distinguisher), { + 'ext_community_set_name': 'set_{:s}'.format(policy_import), + 'ext_community_member' : route_distinguisher, + }), + json_config_rule_set( + # pylint: disable=duplicate-string-formatting-argument + '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format(policy_import, policy_import), { + 'policy_name' : policy_import, + 'statement_name' : 'stm_{:s}'.format(policy_import), + 'ext_community_set_name': 'set_{:s}'.format(policy_import), + 'policy_result' : 'ACCEPT_ROUTE', + }), + + #Associate routing policy to network instance + json_config_rule_set( + '/network_instance[{:s}]/inter_instance_policies[{:s}]'.format(network_instance_name, policy_import), { + 'name' : network_instance_name, + 'import_policy': policy_import, + }), + + #Create routing policy + json_config_rule_set( + '/routing_policy/bgp_defined_set[{:s}_rt_export][{:s}]'.format(policy_export, route_distinguisher), { + 'ext_community_set_name': 'set_{:s}'.format(policy_export), + 'ext_community_member' : route_distinguisher, + }), + json_config_rule_set( + # pylint: disable=duplicate-string-formatting-argument + '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format(policy_export, policy_export), { + 'policy_name' : policy_export, + 'statement_name' : 'stm_{:s}'.format(policy_export), + 'ext_community_set_name': 'set_{:s}'.format(policy_export), + 'policy_result' : 'ACCEPT_ROUTE', + }), + + #Associate routing policy to network instance + json_config_rule_set( + '/network_instance[{:s}]/inter_instance_policies[{:s}]'.format(network_instance_name, policy_export),{ + 'name' : network_instance_name, + 'export_policy': policy_export, + }), + + #Create table connections + json_config_rule_set( + '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format(network_instance_name), { + 'name' : network_instance_name, + 'src_protocol' : 'DIRECTLY_CONNECTED', + 'dst_protocol' : 'BGP', + 'address_family' : 'IPV4', + 'default_import_policy': 'ACCEPT_ROUTE', + }), + + json_config_rule_set( + '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), { + 'name' : network_instance_name, + 'src_protocol' : 'STATIC', + 'dst_protocol' : 'BGP', + 'address_family' : 'IPV4', + 'default_import_policy': 'ACCEPT_ROUTE', + }), + + ] + + for res_key, res_value in endpoint_acls: + json_config_rules.append( + {'action': 1, 'acl': res_value} + ) + return json_config_rules + +def teardown_config_rules( + service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, endpoint_name : str, + service_settings : TreeNode, device_settings : TreeNode, endpoint_settings : TreeNode +) -> List[Dict]: + + if service_settings is None: return [] + if device_settings is None: return [] + if endpoint_settings is None: return [] + + json_settings : Dict = service_settings.value + json_device_settings : Dict = device_settings.value + json_endpoint_settings : Dict = endpoint_settings.value + + settings = (json_settings, json_endpoint_settings, json_device_settings) + + service_short_uuid = service_uuid.split('-')[-1] + network_instance_name = '{:s}-NetInst'.format(service_short_uuid) + #network_interface_desc = '{:s}-NetIf'.format(service_uuid) + #network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid) + + #mtu = get_value('mtu', *settings, default=1450) # 1512 + #address_families = json_settings.get('address_families', [] ) # ['IPV4'] + #bgp_as = get_value('bgp_as', *settings, default=65000) # 65000 + route_distinguisher = json_settings.get('route_distinguisher', '0:0' ) # '60001:801' + #sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1 + #router_id = json_endpoint_settings.get('router_id', '0.0.0.0') # '10.95.0.10' + vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400 + #address_ip = json_endpoint_settings.get('address_ip', '0.0.0.0') # '2.2.2.1' + #address_prefix = json_endpoint_settings.get('address_prefix', 24 ) # 30 + policy_import = json_endpoint_settings.get('policy_AZ', '2' ) # 2 + policy_export = json_endpoint_settings.get('policy_ZA', '7' ) # 30 + + if_subif_name = '{:s}.{:d}'.format(endpoint_name, vlan_id) + + json_config_rules = [ + #Delete table connections + json_config_rule_delete( + '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format(network_instance_name),{ + 'name' : network_instance_name, + 'src_protocol' : 'DIRECTLY_CONNECTED', + 'dst_protocol' : 'BGP', + 'address_family': 'IPV4', + }), + + + json_config_rule_delete( + '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), { + 'name' : network_instance_name, + 'src_protocol' : 'STATIC', + 'dst_protocol' : 'BGP', + 'address_family': 'IPV4', + }), + + #Delete export routing policy + + json_config_rule_delete( + '/routing_policy/policy_definition[{:s}_export]'.format(network_instance_name), { + 'policy_name': '{:s}_export'.format(network_instance_name), + }), + json_config_rule_delete( + '/routing_policy/bgp_defined_set[{:s}_rt_export][{:s}]'.format(policy_export, route_distinguisher), { + 'ext_community_set_name': 'set_{:s}'.format(policy_export), + }), + + #Delete import routing policy + + json_config_rule_delete( + '/routing_policy/policy_definition[{:s}_import]'.format(network_instance_name), { + 'policy_name': '{:s}_import'.format(network_instance_name), + }), + json_config_rule_delete( + '/routing_policy/bgp_defined_set[{:s}_rt_import][{:s}]'.format(policy_import, route_distinguisher), { + 'ext_community_set_name': 'set_{:s}'.format(policy_import), + }), + + #Delete interface; automatically deletes: + # - /interface[]/subinterface[] + json_config_rule_delete('/interface[{:s}]/subinterface[0]'.format(if_subif_name), + { + 'name': if_subif_name, + }), + + #Delete network instance; automatically deletes: + # - /network_instance[]/interface[] + # - /network_instance[]/protocols[] + # - /network_instance[]/inter_instance_policies[] + json_config_rule_delete('/network_instance[{:s}]'.format(network_instance_name), + { + 'name': network_instance_name + }), + ] + return json_config_rules diff --git a/src/service/service/service_handlers/ip_link/ConfigRules_test_ocnos.py b/src/service/service/service_handlers/ip_link/ConfigRules_test_ocnos.py new file mode 100644 index 0000000000000000000000000000000000000000..5fa1d0b5b6931902b4ac50847c90bf67738032ba --- /dev/null +++ b/src/service/service/service_handlers/ip_link/ConfigRules_test_ocnos.py @@ -0,0 +1,337 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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. + +from typing import Dict, List, Tuple +from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set +from service.service.service_handler_api.AnyTreeTools import TreeNode + +def setup_config_rules( + service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, endpoint_name : str, + service_settings : TreeNode, device_settings : TreeNode, endpoint_settings : TreeNode, endpoint_acls : List [Tuple] +) -> List[Dict]: + + if service_settings is None: return [] + if device_settings is None: return [] + if endpoint_settings is None: return [] + + json_settings : Dict = service_settings.value + json_device_settings : Dict = device_settings.value + json_endpoint_settings : Dict = endpoint_settings.value + + mtu = json_settings.get('mtu', 1450 ) # 1512 + #address_families = json_settings.get('address_families', [] ) # ['IPV4'] + bgp_as = json_device_settings.get('bgp_as', 65000 ) # 65000 + + router_id = json_device_settings.get('router_id', '0.0.0.0') # '10.95.0.10' + route_distinguisher = json_device_settings.get('route_distinguisher', '65000:101' ) # '60001:801' + sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1 + vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400 + address_ip = json_endpoint_settings.get('address_ip', '0.0.0.0') # '2.2.2.1' + address_prefix = json_endpoint_settings.get('address_prefix', 24 ) # 30 + + policy_import = json_device_settings.get('policy_AZ', '2' ) # 2 + policy_export = json_device_settings.get('policy_ZA', '7' ) # 30 + #network_interface_desc = '{:s}-NetIf'.format(service_uuid) + network_interface_desc = json_endpoint_settings.get('ni_description','') + #network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid) + network_subinterface_desc = json_endpoint_settings.get('subif_description','') + #service_short_uuid = service_uuid.split('-')[-1] + #network_instance_name = '{:s}-NetInst'.format(service_short_uuid) + network_instance_name = json_endpoint_settings.get('ni_name', service_uuid.split('-')[-1]) #ELAN-AC:1 + + self_bgp_if_name = json_device_settings.get('self_bgp_interface_name', '') + self_bgp_address_ip = json_device_settings.get('self_bgp_interface_address_ip', '') + bgp_address_prefix = json_device_settings.get('bgp_interface_address_prefix', '') + bgp_sub_interface_index = json_device_settings.get('self_bgp_sub_interface_index', 0) + neighbor_bgp_if_address_ip= json_device_settings.get('neighbor_bgp_interface_address_ip', '0.0.0.0') # '2.2.2.1' + + # if_subif_name = '{:s}.{:d}'.format(endpoint_name, 0) + if_subif_name = '{:s}'.format(endpoint_name[5:]) + + json_config_rules = [ + # Configure Interface (not used) + #json_config_rule_set( + # '/interface[{:s}]'.format(endpoint_name), { + # 'name': endpoint_name, + # 'description': network_interface_desc, + # 'mtu': mtu, + #}), + + #Create network instance + json_config_rule_set( + '/network_instance[{:s}]'.format(network_instance_name), { + 'name': network_instance_name, + 'description': network_interface_desc, + 'type': 'L3VRF', + 'route_distinguisher': route_distinguisher, + 'router_id': router_id, + #'address_families': address_families, + }), + + #Add BGP protocol to network instance + json_config_rule_set( + '/network_instance[{:s}]/protocols[BGP]'.format(network_instance_name), { + 'name': network_instance_name, + 'protocol_name': bgp_as, + 'identifier': 'BGP', + 'type': 'L3VRF', + 'as': bgp_as, + 'router_id': router_id, + 'neighbors': [{'ip_address': neighbor_bgp_if_address_ip, 'remote_as': bgp_as}] + }), + + #Add DIRECTLY CONNECTED protocol to network instance + json_config_rule_set( + '/network_instance[{:s}]/protocols[DIRECTLY_CONNECTED]'.format(network_instance_name), { + 'name': network_instance_name, + 'identifier': 'DIRECTLY_CONNECTED', + 'protocol_name': 'DIRECTLY_CONNECTED', + }), + + #Add STATIC protocol to network instance + json_config_rule_set( + '/network_instance[{:s}]/protocols[STATIC]'.format(network_instance_name), { + 'name': network_instance_name, + 'identifier': 'STATIC', + 'protocol_name': 'STATIC', + }), + + #Create interface with subinterface (without IP address) + json_config_rule_set( + '/interface[{:s}]/subinterface[{:d}]'.format(if_subif_name, sub_interface_index), { + 'name' : if_subif_name, + 'type' :'ethernetCsmacd', + 'mtu' : mtu, + 'index' : sub_interface_index, + 'description': network_subinterface_desc, + 'vlan_id' : vlan_id, + }), + + #Associate interface to network instance + json_config_rule_set( + '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_subif_name), { + 'name' : network_instance_name, + 'type' : 'L3VRF', + 'id' : if_subif_name, + 'interface' : if_subif_name, + 'subinterface' : sub_interface_index, + 'address_ip' : address_ip, + 'address_prefix': address_prefix, + }), + + #Create interface with subinterface (with IP address) + json_config_rule_set( + '/interface[{:s}]/subinterface[{:d}]'.format(if_subif_name, sub_interface_index), { + 'name' : if_subif_name, + 'type' :'ethernetCsmacd', + 'mtu' : mtu, + 'index' : sub_interface_index, + 'description' : network_subinterface_desc, + 'vlan_id' : vlan_id, + 'address_ip' : address_ip, + 'address_prefix': address_prefix, + }), + + json_config_rule_set( + '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, self_bgp_if_name), { + 'name' : network_instance_name, + 'type' : 'L3VRF', + 'id' : self_bgp_if_name, + 'interface' : self_bgp_if_name, + 'subinterface': bgp_sub_interface_index, + 'address_ip' : self_bgp_address_ip, + 'address_prefix': bgp_address_prefix, + }), + + #Create routing policy + json_config_rule_set( + '/routing_policy/bgp_defined_set[{:s}_rt_import][{:s}]'.format(policy_import,route_distinguisher), { + 'ext_community_set_name': 'set_{:s}'.format(policy_import), + 'ext_community_member' : route_distinguisher, + }), + json_config_rule_set( + # pylint: disable=duplicate-string-formatting-argument + '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format(policy_import, policy_import), { + 'policy_name' : policy_import, + 'statement_name' : 'stm_{:s}'.format(policy_import), # OCNOS: '10', + 'ext_community_set_name': 'set_{:s}'.format(policy_import), + 'policy_result' : 'ACCEPT_ROUTE', + }), + + #Associate routing policy to network instance + json_config_rule_set( + '/network_instance[{:s}]/inter_instance_policies[{:s}]'.format(network_instance_name, policy_import), { + 'name' : network_instance_name, + 'import_policy': policy_import, + }), + + #Create routing policy + json_config_rule_set( + '/routing_policy/bgp_defined_set[{:s}_rt_export][{:s}]'.format(policy_export, route_distinguisher), { + 'ext_community_set_name': 'set_{:s}'.format(policy_export), + 'ext_community_member' : route_distinguisher, + }), + json_config_rule_set( + # pylint: disable=duplicate-string-formatting-argument + '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format(policy_export, policy_export), { + 'policy_name' : policy_export, + 'statement_name' : 'stm_{:s}'.format(policy_export), # OCNOS: '10', + 'ext_community_set_name': 'set_{:s}'.format(policy_export), + 'policy_result' : 'ACCEPT_ROUTE', + }), + + #Associate routing policy to network instance + json_config_rule_set( + '/network_instance[{:s}]/inter_instance_policies[{:s}]'.format(network_instance_name, policy_export),{ + 'name' : network_instance_name, + 'export_policy': policy_export, + }), + + #Create table connections + json_config_rule_set( + '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format(network_instance_name), { + 'name' : network_instance_name, + 'src_protocol' : 'DIRECTLY_CONNECTED', + 'dst_protocol' : 'BGP', + 'address_family' : 'IPV4', + 'default_import_policy': 'ACCEPT_ROUTE', + 'as' : bgp_as, + }), + + json_config_rule_set( + '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), { + 'name' : network_instance_name, + 'src_protocol' : 'STATIC', + 'dst_protocol' : 'BGP', + 'address_family' : 'IPV4', + 'default_import_policy': 'ACCEPT_ROUTE', + 'as' : bgp_as, + }), + + ] + + for res_key, res_value in endpoint_acls: + json_config_rules.append( + {'action': 1, 'acl': res_value} + ) + return json_config_rules + +def teardown_config_rules( + service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, endpoint_name : str, + service_settings : TreeNode, device_settings : TreeNode, endpoint_settings : TreeNode +) -> List[Dict]: + + if service_settings is None: return [] + if device_settings is None: return [] + if endpoint_settings is None: return [] + + json_settings : Dict = service_settings.value + json_device_settings : Dict = device_settings.value + json_endpoint_settings : Dict = endpoint_settings.value + + service_short_uuid = service_uuid.split('-')[-1] + # network_instance_name = '{:s}-NetInst'.format(service_short_uuid) + network_instance_name = json_endpoint_settings.get('ni_name', service_short_uuid) #ELAN-AC:1 + #network_interface_desc = '{:s}-NetIf'.format(service_uuid) + # network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid) + network_subinterface_desc = '' + + mtu = json_settings.get('mtu', 1450 ) # 1512 + #address_families = json_settings.get('address_families', [] ) # ['IPV4'] + #bgp_as = json_device_settings.get('bgp_as', 65000 ) # 65000 + route_distinguisher = json_device_settings.get('route_distinguisher', '0:0' ) # '60001:801' + sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1 + #router_id = json_device_settings.get('router_id', '0.0.0.0') # '10.95.0.10' + vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400 + address_ip = json_endpoint_settings.get('address_ip', '0.0.0.0') # '2.2.2.1' + address_prefix = json_endpoint_settings.get('address_prefix', 24 ) # 30 + policy_import = json_device_settings.get('policy_AZ', '2' ) # 2 + policy_export = json_device_settings.get('policy_ZA', '7' ) # 30 + + self_bgp_if_name = json_device_settings.get('self_bgp_interface_name', '') + self_bgp_address_ip = json_device_settings.get('self_bgp_interface_address_ip', '') + bgp_address_prefix = json_device_settings.get('bgp_interface_address_prefix', '') + bgp_sub_interface_index = json_device_settings.get('self_bgp_sub_interface_index', 0) + + # if_subif_name = '{:s}.{:d}'.format(endpoint_name, vlan_id) + if_subif_name = '{:s}'.format(endpoint_name[5:]) + + json_config_rules = [ + #Delete export routing policy + json_config_rule_delete( + # pylint: disable=duplicate-string-formatting-argument + '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format(policy_export, policy_export), { + 'policy_name' : policy_export, + 'statement_name' : 'stm_{:s}'.format(policy_export), # OCNOS: '10', + 'ext_community_set_name': 'set_{:s}'.format(policy_export), + 'policy_result' : 'ACCEPT_ROUTE', + }), + json_config_rule_delete( + '/routing_policy/bgp_defined_set[{:s}_rt_export][{:s}]'.format(policy_export, route_distinguisher), { + 'ext_community_set_name': 'set_{:s}'.format(policy_export), + 'ext_community_member' : route_distinguisher, + }), + + #Delete import routing policy + json_config_rule_delete( + # pylint: disable=duplicate-string-formatting-argument + '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format(policy_import, policy_import), { + 'policy_name' : policy_import, + 'statement_name' : 'stm_{:s}'.format(policy_import), # OCNOS: '10', + 'ext_community_set_name': 'set_{:s}'.format(policy_import), + 'policy_result' : 'ACCEPT_ROUTE', + }), + json_config_rule_delete( + '/routing_policy/bgp_defined_set[{:s}_rt_import][{:s}]'.format(policy_import, route_distinguisher), { + 'ext_community_set_name': 'set_{:s}'.format(policy_import), + 'ext_community_member' : route_distinguisher, + }), + + #Delete interface; automatically deletes: + # - /interface[]/subinterface[] + # json_config_rule_delete('/interface[{:s}]/subinterface[0]'.format(if_subif_name), + # { + # 'name': if_subif_name, + # }), + + #Delete network instance; automatically deletes: + # - /network_instance[]/interface[] + # - /network_instance[]/protocols[] + # - /network_instance[]/inter_instance_policies[] + + #Associate interface to network instance + json_config_rule_set( + '/network_instance[{:s}]/interface[{:s}]'.format('default', if_subif_name), { + 'name' : 'default', + 'id' : if_subif_name, + 'interface' : if_subif_name, + 'subinterface': sub_interface_index, + 'address_ip' : address_ip, + 'address_prefix': address_prefix, + }), + json_config_rule_set( + '/network_instance[{:s}]/interface[{:s}]'.format('default', self_bgp_if_name), { + 'name' : 'default', + 'id' : self_bgp_if_name, + 'interface' : self_bgp_if_name, + 'subinterface': bgp_sub_interface_index, + 'address_ip' : self_bgp_address_ip, + 'address_prefix': bgp_address_prefix, + }), + json_config_rule_delete('/network_instance[{:s}]'.format(network_instance_name), + { + 'name': network_instance_name + }), + ] + return json_config_rules diff --git a/src/service/service/service_handlers/ip_link/IP_LinkServiceHandler.py b/src/service/service/service_handlers/ip_link/IP_LinkServiceHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..51b454051c97a54571ea3f271b9cd40bb3995f36 --- /dev/null +++ b/src/service/service/service_handlers/ip_link/IP_LinkServiceHandler.py @@ -0,0 +1,167 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 json, logging +from typing import Any, List, Optional, Tuple, Union +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.proto.context_pb2 import ConfigRule, DeviceId, Service +from common.tools.object_factory.Device import json_device_id +from common.type_checkers.Checkers import chk_type +from service.service.service_handler_api.Tools import get_device_endpoint_uuids, get_endpoint_matching +from service.service.service_handler_api._ServiceHandler import _ServiceHandler +from service.service.service_handler_api.SettingsHandler import SettingsHandler +from service.service.task_scheduler.TaskExecutor import TaskExecutor +from .ConfigRules import setup_config_rules, teardown_config_rules + +LOGGER = logging.getLogger(__name__) + +METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'l3nm_openconfig'}) + +class IP_LinkServiceHandler(_ServiceHandler): + def __init__( # pylint: disable=super-init-not-called + self, service : Service, task_executor : TaskExecutor, **settings + ) -> None: + self.__service = service + self.__task_executor = task_executor + self.__settings_handler = SettingsHandler(service.service_config, **settings) + + @metered_subclass_method(METRICS_POOL) + def SetEndpoint( + self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None + ) -> List[Union[bool, Exception]]: + chk_type('endpoints', endpoints, list) + if len(endpoints) == 0: return [] + + service_uuid = self.__service.service_id.service_uuid.uuid + settings = self.__settings_handler.get('/settings') + + results = [] + for endpoint in endpoints: + try: + device_uuid, endpoint_uuid = get_device_endpoint_uuids(endpoint) + + device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) + device_settings = self.__settings_handler.get_device_settings(device_obj) + endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid) + endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj) + endpoint_acls = self.__settings_handler.get_endpoint_acls(device_obj, endpoint_obj) + endpoint_ip_link = self.__settings_handler.get_endpoint_ip_link(device_obj, endpoint_obj) + endpoint_name = endpoint_obj.name + + LOGGER.warning('DATOS({:s})'.format(str(endpoint_ip_link))) + + json_config_rules = setup_config_rules( + service_uuid, connection_uuid, device_uuid, endpoint_uuid, endpoint_name, + settings, device_settings, endpoint_settings, endpoint_acls, endpoint_ip_link) + + if len(json_config_rules) > 0: + del device_obj.device_config.config_rules[:] + for json_config_rule in json_config_rules: + device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule)) + self.__task_executor.configure_device(device_obj) + + results.append(True) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint))) + results.append(e) + + return results + + @metered_subclass_method(METRICS_POOL) + def DeleteEndpoint( + self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None + ) -> List[Union[bool, Exception]]: + chk_type('endpoints', endpoints, list) + if len(endpoints) == 0: return [] + + service_uuid = self.__service.service_id.service_uuid.uuid + settings = self.__settings_handler.get('/settings') + + results = [] + for endpoint in endpoints: + try: + device_uuid, endpoint_uuid = get_device_endpoint_uuids(endpoint) + + device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) + device_settings = self.__settings_handler.get_device_settings(device_obj) + endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid) + endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj) + endpoint_name = endpoint_obj.name + + json_config_rules = teardown_config_rules( + service_uuid, connection_uuid, device_uuid, endpoint_uuid, endpoint_name, + settings, device_settings, endpoint_settings) + + if len(json_config_rules) > 0: + del device_obj.device_config.config_rules[:] + for json_config_rule in json_config_rules: + device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule)) + self.__task_executor.configure_device(device_obj) + + results.append(True) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint))) + results.append(e) + + return results + + @metered_subclass_method(METRICS_POOL) + def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('constraints', constraints, list) + if len(constraints) == 0: return [] + + msg = '[SetConstraint] Method not implemented. Constraints({:s}) are being ignored.' + LOGGER.warning(msg.format(str(constraints))) + return [True for _ in range(len(constraints))] + + @metered_subclass_method(METRICS_POOL) + def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('constraints', constraints, list) + if len(constraints) == 0: return [] + + msg = '[DeleteConstraint] Method not implemented. Constraints({:s}) are being ignored.' + LOGGER.warning(msg.format(str(constraints))) + return [True for _ in range(len(constraints))] + + @metered_subclass_method(METRICS_POOL) + def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('resources', resources, list) + if len(resources) == 0: return [] + + results = [] + for resource in resources: + try: + resource_value = json.loads(resource[1]) + self.__settings_handler.set(resource[0], resource_value) + results.append(True) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource))) + results.append(e) + + return results + + @metered_subclass_method(METRICS_POOL) + def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('resources', resources, list) + if len(resources) == 0: return [] + + results = [] + for resource in resources: + try: + self.__settings_handler.delete(resource[0]) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource))) + results.append(e) + + return results diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/run_tests_locally-nbi-ietf-l3vpn.sh b/src/service/service/service_handlers/ip_link/__init__.py old mode 100755 new mode 100644 similarity index 56% rename from src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/run_tests_locally-nbi-ietf-l3vpn.sh rename to src/service/service/service_handlers/ip_link/__init__.py index 0bd133e16caac438c8da1a5795fce89e99230c9a..3ee6f7071f145e06c3aeaefc09a43ccd88e619e3 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/run_tests_locally-nbi-ietf-l3vpn.sh +++ b/src/service/service/service_handlers/ip_link/__init__.py @@ -1,5 +1,4 @@ -#!/bin/bash -# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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. @@ -13,13 +12,3 @@ # 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/service/service/task_scheduler/TaskScheduler.py b/src/service/service/task_scheduler/TaskScheduler.py index b492b4581b05cb34bed624efd9be2d4f62da5856..a791f1e9d29e119269ab15195a7b6aec00c308ad 100644 --- a/src/service/service/task_scheduler/TaskScheduler.py +++ b/src/service/service/task_scheduler/TaskScheduler.py @@ -245,6 +245,8 @@ class TasksScheduler: for task_key in ordered_task_keys: str_task_name = ('DRY ' if dry_run else '') + str(task_key) LOGGER.debug('[execute_all] starting task {:s}'.format(str_task_name)) + LOGGER.debug('[execute_all] key={:s}'.format(str(task_key))) + LOGGER.debug('[execute_all] key={:s}'.format(str(task_key))) task = self._tasks.get(task_key) succeeded = True if dry_run else task.execute() results.append(succeeded) diff --git a/src/ztp/target/generated-sources/grpc/context/ContextOuterClass.java b/src/ztp/target/generated-sources/grpc/context/ContextOuterClass.java index 4593770498216267b8d2f95dd728fccfbb9dc134..b0742e306183a7d5da1a03a54d7734b1e398d75c 100644 --- a/src/ztp/target/generated-sources/grpc/context/ContextOuterClass.java +++ b/src/ztp/target/generated-sources/grpc/context/ContextOuterClass.java @@ -504,6 +504,10 @@ public final class ContextOuterClass { * <code>SERVICETYPE_OPTICAL_CONNECTIVITY = 6;</code> */ SERVICETYPE_OPTICAL_CONNECTIVITY(6), + /** + * <code>SERVICETYPE_IPLINK = 6;</code> + */ + SERVICETYPE_IPLINK(7), UNRECOGNIZED(-1); /** @@ -541,6 +545,11 @@ public final class ContextOuterClass { */ public static final int SERVICETYPE_OPTICAL_CONNECTIVITY_VALUE = 6; + /** + * <code>SERVICETYPE_IPLINK = 7;</code> + */ + public static final int SERVICETYPE_IPLINK_VALUE = 7; + public final int getNumber() { if (this == UNRECOGNIZED) { throw new java.lang.IllegalArgumentException("Can't get the number of an unknown enum value."); @@ -578,6 +587,8 @@ public final class ContextOuterClass { return SERVICETYPE_E2E; case 6: return SERVICETYPE_OPTICAL_CONNECTIVITY; + case 7: + return SERVICETYPE_IPLINK; default: return null; }