diff --git a/src/device/service/Tools.py b/src/device/service/Tools.py index 8eb25578488dd0fe5b1781a0e129709b86df69fa..6e6192b12f3c31886f97393b924aed1770549c24 100644 --- a/src/device/service/Tools.py +++ b/src/device/service/Tools.py @@ -332,9 +332,8 @@ def compute_rules_to_add_delete( 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) + IP_LINK_KEY_TEMPLATE = '/device[{:s}]/endpoint[{:s}]/ip_link_ruleset' + key_or_path = IP_LINK_KEY_TEMPLATE.format(device_uuid, endpoint_uuid) request_config_rules.append(( config_rule.action, key_or_path, grpc_message_to_json(config_rule.ip_link) )) diff --git a/src/device/service/drivers/openconfig/OpenConfigDriver.py b/src/device/service/drivers/openconfig/OpenConfigDriver.py index fd36e2dc40e38a125f1812f00eeb304106a40c8a..125c34076dfa1a836f7efe3060d151c781cba213 100644 --- a/src/device/service/drivers/openconfig/OpenConfigDriver.py +++ b/src/device/service/drivers/openconfig/OpenConfigDriver.py @@ -30,7 +30,7 @@ from device.service.driver_api.Exceptions import UnsupportedResourceKeyException from device.service.driver_api._Driver import _Driver from device.service.driver_api.AnyTreeTools import TreeNode, get_subnode, set_subnode_value #dump_subtree #from .Tools import xml_pretty_print, xml_to_dict, xml_to_file -from .templates import ALL_RESOURCE_KEYS, EMPTY_CONFIG, compose_config, get_filter, parse, cli_compose_config +from .templates import ALL_RESOURCE_KEYS, EMPTY_CONFIG, compose_config, get_filter, parse, cli_compose_config, ufi_interface, cisco_interface from .RetryDecorator import retry DEBUG_MODE = False @@ -212,10 +212,22 @@ def edit_config( ): str_method = 'DeleteConfig' if delete else 'SetConfig' results = [] - if "L2VSI" in resources[0][1] and netconf_handler.vendor == "CISCO": + if netconf_handler.vendor == "CISCO": + if "L2VSI" in resources[0][1]: + #Configure by CLI + logger.warning("CLI Configuration") + cli_compose_config(resources, delete=delete, host= netconf_handler._NetconfSessionHandler__address, user=netconf_handler._NetconfSessionHandler__username, passw=netconf_handler._NetconfSessionHandler__password) + for i,resource in enumerate(resources): + results.append(True) + else: + logger.warning("CLI Configuration CISCO INTERFACE") + cisco_interface(resources, delete=delete, host= netconf_handler._NetconfSessionHandler__address, user=netconf_handler._NetconfSessionHandler__username, passw=netconf_handler._NetconfSessionHandler__password) + for i,resource in enumerate(resources): + results.append(True) + elif netconf_handler.vendor == "UFISPACE": #Configure by CLI - logger.warning("CLI Configuration") - cli_compose_config(resources, delete=delete, host= netconf_handler._NetconfSessionHandler__address, user=netconf_handler._NetconfSessionHandler__username, passw=netconf_handler._NetconfSessionHandler__password) + logger.warning("CLI Configuration: {:s}".format(resources)) + ufi_interface(resources, delete=delete) for i,resource in enumerate(resources): results.append(True) else: diff --git a/src/device/service/drivers/openconfig/templates/IP_LINK/IP_LINK_multivendor.py b/src/device/service/drivers/openconfig/templates/IP_LINK/IP_LINK_multivendor.py new file mode 100755 index 0000000000000000000000000000000000000000..f4f6ad4dd0000e6b78352a129c87dc0c56aa7d53 --- /dev/null +++ b/src/device/service/drivers/openconfig/templates/IP_LINK/IP_LINK_multivendor.py @@ -0,0 +1,59 @@ +# 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 yattag import Doc, indent + +def ip_link_mgmt(data,vendor, delete): + doc, tag, text = Doc().tagtext() + + ID = data['endpoint_id']['endpoint_uuid']['uuid'] + DATA = data["rule_set"] + + with tag('interfaces', xmlns="http://openconfig.net/yang/interfaces"): + if delete == True: + with tag('interface' ,'xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="delete"'): + with tag('name'):text(ID) + else: + with tag('interface'): + with tag('name'):text(ID) + with tag('config'): + with tag('name'):text(ID) + with tag('type', 'xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type"'):text('ianaift:l3ipvlan') + with tag('enabled'):text('true') + with tag('subinterfaces'): + with tag('subinterface'): + if vendor is None or vendor == 'ADVA': + with tag('index'): text('0') + with tag('config'): + with tag('index'): text('0') + if vendor == 'ADVA' and not 'vlan'in data: + with tag('untagged-allowed', 'xmlns="http://www.advaoptical.com/cim/adva-dnos-oc-interfaces"'):text('true') + with tag('vlan', xmlns="http://openconfig.net/yang/vlan"): + with tag('match'): + with tag('single-tagged'): + with tag('config'): + with tag('vlan-id'):text(DATA['vlan']) + with tag('ipv4', xmlns="http://openconfig.net/yang/interfaces/ip"): + with tag('addresses'): + with tag('address'): + with tag('ip'):text(DATA['ip']) + with tag('config'): + with tag('ip'):text(DATA['ip']) + with tag('prefix-length'):text(DATA['mask']) + result = indent( + doc.getvalue(), + indentation = ' '*2, + newline = '\r\n' + ) + return result \ No newline at end of file diff --git a/src/device/service/drivers/openconfig/templates/Tools.py b/src/device/service/drivers/openconfig/templates/Tools.py index c4ef22b1e3c11f1e512026bea8e2122ab703a9e5..bfa7f558b4f81207613462eb67a26448f9b52579 100644 --- a/src/device/service/drivers/openconfig/templates/Tools.py +++ b/src/device/service/drivers/openconfig/templates/Tools.py @@ -15,9 +15,10 @@ import json import lxml.etree as ET from typing import Collection, Dict, Any -from .ACL.ACL_multivendor import acl_mgmt +from .ACL.ACL_multivendor import acl_mgmt +from .IP_LINK.IP_LINK_multivendor import ip_link_mgmt from .VPN.Network_instance_multivendor import create_NI, associate_virtual_circuit, associate_RP_to_NI, add_protocol_NI, create_table_conns, associate_If_to_NI -from .VPN.Interfaces_multivendor import create_If_SubIf +from .VPN.Interfaces_multivendor import create_If_SubIf from .VPN.Routing_policy import create_rp_def, create_rp_statement def add_value_from_tag(target : Dict, field_name: str, field_value : ET.Element, cast=None) -> None: @@ -70,7 +71,7 @@ def generate_templates(resource_key: str, resource_value: str, delete: bool,vend else: result_templates.append(create_NI(data,vendor,delete)) - if "interface" in list_resource_key[1]: # interface rules management + elif "interface" in list_resource_key[1]: # interface rules management data: Dict[str, Any] = json.loads(resource_value) #data['DEL'] = delete if "subinterface" in resource_key: @@ -83,8 +84,10 @@ def generate_templates(resource_key: str, resource_value: str, delete: bool,vend result_templates.append(create_rp_def(data, delete)) else: result_templates.append(create_rp_statement(data, delete)) - else: - if "acl_ruleset" in resource_key: # acl rules management + elif "acl_ruleset" in resource_key: # acl rules management result_templates.extend(acl_mgmt(resource_value,vendor, delete)) + else: + if "ip_link" in resource_key: + result_templates.append(ip_link_mgmt(resource_value,vendor,delete)) return result_templates \ No newline at end of file diff --git a/src/device/service/drivers/openconfig/templates/__init__.py b/src/device/service/drivers/openconfig/templates/__init__.py index 969ca9f60bd870ca7474f8b06a8b1948a03e84f6..f854f5d431ed18a1cacf2be9a336fb184273709a 100644 --- a/src/device/service/drivers/openconfig/templates/__init__.py +++ b/src/device/service/drivers/openconfig/templates/__init__.py @@ -252,4 +252,122 @@ def cli_compose_config(resources, delete: bool, host: str, user: str, passw: str # Close the SSH client ssh_client.close() - \ No newline at end of file + +def ufi_interface(resources, delete: bool): #Method used for configuring via CLI directly L2VPN in CISCO devices + + key_value_data = {} + + for path, json_str in resources: + key_value_data[path] = json_str + + # Iterate through the resources and extract parameter values dynamically + + + # initialize the SSH client + ssh_client = paramiko.SSHClient() + ssh_client.load_system_host_keys() + # add to known hosts + ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + try: + ssh_client.connect(hostname='10.95.90.75', username='dnroot', password='dnroot', look_for_keys=False) + #print("Connection successful") + LOGGER.warning("Connection successful") + except: + #print("[!] Cannot connect to the SSH Server") + LOGGER.warning("[!] Cannot connect to the SSH Server") + exit() + interface = 'ge100-0/0/3/1' + ip = '1.1.1.1' + mask = '24' + vlan = '1212' + try: + # Open an SSH shell + channel = ssh_client.invoke_shell() + time.sleep(5) + channel.send('config\n') + time.sleep(1) + channel.send(f'interfaces {interface} \n') + time.sleep(1) + channel.send('admin-state enabled \n') + time.sleep(1) + channel.send(f'ipv4-address {ip}/{mask} \n') + time.sleep(1) + channel.send(f'vlan-id {vlan} \n') + time.sleep(1) + channel.send('commit\n') + time.sleep(1) + + # Capturar la salida del comando + output = channel.recv(65535).decode('utf-8') + #print(output) + LOGGER.warning(output) + # Close the SSH shell + channel.close() + + except Exception as e: + LOGGER.exception(f"Error with the CLI configuration: {e}") + + # Close the SSH client + ssh_client.close() + + + +def cisco_interface(resources, delete: bool, host: str, user: str, passw: str): #Method used for configuring via CLI directly L2VPN in CISCO devices + + key_value_data = {} + + for path, json_str in resources: + key_value_data[path] = json_str + + # Iterate through the resources and extract parameter values dynamically + + + # initialize the SSH client + ssh_client = paramiko.SSHClient() + ssh_client.load_system_host_keys() + # add to known hosts + ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + try: + ssh_client.connect(hostname='10.90.95.150', username='cisco', password='cisco123', look_for_keys=False) + #print("Connection successful") + LOGGER.warning("Connection successful") + except: + #print("[!] Cannot connect to the SSH Server") + LOGGER.warning("[!] Cannot connect to the SSH Server") + exit() + interface = 'FourHundredGigE0/0/0/10.1212' + ip = '1.1.1.1' + mask = '24' + vlan = '1212' + try: + # Open an SSH shell + channel = ssh_client.invoke_shell() + time.sleep(1) + channel.send('config\n') + time.sleep(0.1) + channel.send(f'interface {interface} \n') + time.sleep(0.1) + channel.send('no shutdown\n') + time.sleep(0.1) + channel.send(f'ipv4 address {ip}/{mask} \n') + time.sleep(0.1) + channel.send(f'encapsulation dot1q {vlan} \n') + time.sleep(0.1) + channel.send('commit\n') + time.sleep(0.1) + + + # Capturar la salida del comando + output = channel.recv(65535).decode('utf-8') + #print(output) + LOGGER.warning(output) + # Close the SSH shell + channel.close() + + except Exception as e: + LOGGER.exception(f"Error with the CLI configuration: {e}") + + # Close the SSH client + ssh_client.close() diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py index f96291e20b0d93fbf8102295d9ed8347e301760a..2c5f6c70fbc5c608bee0b7a441df41a7bdbec5b6 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py @@ -369,7 +369,7 @@ def generate_neighbor_endpoint_config_rules( LOGGER.debug('[generate_neighbor_endpoint_config_rules] IP_LINK: {:s}'.format(str(config_rule))) resource_value : Dict = config_rule['ip_link'] generated_config_rule = copy.deepcopy(config_rule) - generated_config_rule['ip_link'] = json.dumps(resource_value) + generated_config_rule['ip_link'] = resource_value generated_config_rules.append(generated_config_rule) LOGGER.debug('[generate_neighbor_endpoint_config_rules] generated_config_rules={:s}'.format(str(generated_config_rules))) diff --git a/src/service/service/service_handler_api/SettingsHandler.py b/src/service/service/service_handler_api/SettingsHandler.py index 8bfa1cd8a983ad20323525b38ab8d898c99dbde1..6bdbebf1ff56521fe006565b434c17f7deb5e812 100644 --- a/src/service/service/service_handler_api/SettingsHandler.py +++ b/src/service/service/service_handler_api/SettingsHandler.py @@ -50,8 +50,9 @@ class SettingsHandler: elif kind == 'ip_link': 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_KEY_TEMPLATE = '/device[{:s}]/endpoint[{:s}]/ip_link' - key_or_path = IP_LINK_KEY_TEMPLATE.format(device_uuid, endpoint_uuid,) + endpoint_name, endpoint_index = extract_endpoint_index(endpoint_uuid) + IP_LINK_KEY_TEMPLATE = '/device[{:s}]/endpoint[{:s}]/subindex[{:d}]/ip_link' + key_or_path = IP_LINK_KEY_TEMPLATE.format(device_uuid, endpoint_name, endpoint_index) value = config_rule.ip_link else: MSG = 'Unsupported Kind({:s}) in ConfigRule({:s})' @@ -124,7 +125,10 @@ class SettingsHandler: if not res_key.startswith(IP_LINK_KEY_TEMPLATE): continue if not "ip_link" in res_key: continue ip_links.append((res_key, res_value)) - return None + iplink_index = extract_index(res_value) + if not 'subindex[{:d}]'.format(iplink_index) in res_key: continue + ip_links.append((res_key, res_value)) + return ip_links def set(self, key_or_path : Union[str, List[str]], value : Any) -> None: set_subnode_value(self.__resolver, self.__config, key_or_path, value) diff --git a/src/service/service/service_handler_api/Tools.py b/src/service/service/service_handler_api/Tools.py index 7faa95312a9923c924f1a42661f87380cc731352..9e5927cfecc4b384d25e590c46eacf659c2a3754 100644 --- a/src/service/service/service_handler_api/Tools.py +++ b/src/service/service/service_handler_api/Tools.py @@ -61,16 +61,16 @@ def get_device_endpoint_uuids(endpoint : Tuple[str, str, Optional[str]]) -> Tupl return device_uuid, endpoint_uuid def extract_endpoint_index(endpoint_name : str, default_index=0) -> Tuple[str, int]: - RE_PATTERN = '^(eth\-[0-9]+(?:\/[0-9]+)*)(?:\.([0-9]+))?$' + RE_PATTERN = r'^(eth\-[0-9]+(?:\/[0-9]+)*)(?:\.([0-9]+))?$' m = re.match(RE_PATTERN, endpoint_name) if m is None: return endpoint_name, default_index endpoint_name, index = m.groups() if index is not None: index = int(index) return endpoint_name, index -def extract_index(res_value : str) -> int: - acl_value = grpc_message_to_json(res_value,use_integers_for_enums=True) - endpoint = acl_value.split("'endpoint_uuid': {'uuid': '") +def extract_index(res_value : Any) -> int: + res_value = grpc_message_to_json(res_value,use_integers_for_enums=True) + endpoint = res_value['endpoint_id']['endpoint_uuid']['uuid'] endpoint = endpoint[1].split("'}") _ , index = extract_endpoint_index(endpoint[0]) return index diff --git a/src/service/service/service_handlers/ip_link/ConfigRules.py b/src/service/service/service_handlers/ip_link/ConfigRules.py index 46c7877fed1044e3d029a5c9500fd5ac23ef6a3f..96eab0dbf199fe1012f75d05078ddafb5e90b2f5 100644 --- a/src/service/service/service_handlers/ip_link/ConfigRules.py +++ b/src/service/service/service_handlers/ip_link/ConfigRules.py @@ -12,9 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging 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 +LOGGER = logging.getLogger(__name__) def get_value(field_name : str, *containers, default=None) -> Optional[Any]: if len(containers) == 0: raise Exception('No containers specified') @@ -24,23 +26,18 @@ def get_value(field_name : str, *containers, default=None) -> Optional[Any]: 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] + endpoint_name : str, 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 - json_config_rules = [] + json_config_rules = [ + ] + + for res_key, res_value in endpoint_ip_link: + json_config_rules.append( + {'action': 1, 'ip_link': res_value} + ) + return json_config_rules def teardown_config_rules( @@ -58,24 +55,6 @@ def teardown_config_rules( 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 = [] 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 index 832b386c54694d652400238b58333f0721a2bfd1..cbb42a5c89c8db32c327e13122b219c1c1ec3c55 100644 --- a/src/service/service/service_handlers/ip_link/IP_LinkServiceHandler.py +++ b/src/service/service/service_handlers/ip_link/IP_LinkServiceHandler.py @@ -52,15 +52,11 @@ class IP_LinkServiceHandler(_ServiceHandler): 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 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) + endpoint_name, endpoint_ip_link) if len(json_config_rules) > 0: del device_obj.device_config.config_rules[:]