Loading src/device/service/drivers/oc_driver/OCDriver.py +64 −10 Original line number Diff line number Diff line Loading @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. import time import json import logging, pytz, queue, re, threading #import lxml.etree as ET import logging, pytz, re, threading from typing import Any, List, Tuple, Union from apscheduler.executors.pool import ThreadPoolExecutor from apscheduler.jobstores.memory import MemoryJobStore Loading @@ -22,12 +22,11 @@ from apscheduler.schedulers.background import BackgroundScheduler from ncclient.manager import Manager, connect_ssh from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.tools.client.RetryDecorator import delay_exponential from common.type_checkers.Checkers import chk_type from common.type_checkers.Checkers import chk_length, chk_string, chk_type from device.service.driver_api.Exceptions import UnsupportedResourceKeyException from device.service.driver_api._Driver import _Driver from device.service.driver_api.AnyTreeTools import TreeNode from .templates import compose_config, cli_compose_config, ufi_interface, cisco_interface from .templates.VPN.common import seperate_port_config #from .Tools import xml_pretty_print, xml_to_dict, xml_to_file from .templates.VPN.roadms import ( create_optical_band, disable_media_channel, delete_optical_band, create_media_channel_v2 ) Loading @@ -36,11 +35,10 @@ from .RetryDecorator import retry from context.client.ContextClient import ContextClient from common.proto.context_pb2 import OpticalConfig from .templates.discovery_tool.transponders import transponder_values_extractor from .templates.discovery_tool.roadms import roadm_values_extractor, extract_media_channels from .templates.discovery_tool.roadms import roadm_values_extractor from .templates.discovery_tool.open_roadm import openroadm_values_extractor from .templates.VPN.openroadm import network_media_channel_handler DEBUG_MODE = False logging.getLogger('ncclient.manager').setLevel(logging.DEBUG if DEBUG_MODE else logging.WARNING) logging.getLogger('ncclient.transport.ssh').setLevel(logging.DEBUG if DEBUG_MODE else logging.WARNING) Loading @@ -51,9 +49,6 @@ logging.getLogger('monitoring-client').setLevel(logging.INFO if DEBUG_MODE else RE_GET_ENDPOINT_FROM_INTERFACE_KEY = re.compile(r'.*interface\[([^\]]+)\].*') RE_GET_ENDPOINT_FROM_INTERFACE_XPATH = re.compile(r".*interface\[oci\:name\='([^\]]+)'\].*") # Collection of samples through NetConf is very slow and each request collects all the data. # Populate a cache periodically (when first interface is interrogated). # Evict data after some seconds, when data is considered as outdated SAMPLE_EVICTION_SECONDS = 30.0 # seconds SAMPLE_RESOURCE_KEY = 'interfaces/interface/state/counters' Loading Loading @@ -201,6 +196,65 @@ def edit_config( #results[i] = True results.append(result) 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: {:s}".format(resources)) ufi_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) else: for i,resource in enumerate(resources): str_resource_name = 'resources[#{:d}]'.format(i) try: logger.debug('[{:s}] resource = {:s}'.format(str_method, str(resource))) chk_type(str_resource_name, resource, (list, tuple)) chk_length(str_resource_name, resource, min_length=2, max_length=2) resource_key,resource_value = resource chk_string(str_resource_name + '.key', resource_key, allow_empty=False) str_config_messages = compose_config( # get template for configuration resource_key, resource_value, delete=delete, vendor=netconf_handler.vendor, message_renderer=netconf_handler.message_renderer) for str_config_message in str_config_messages: # configuration of the received templates if str_config_message is None: raise UnsupportedResourceKeyException(resource_key) logger.debug('[{:s}] str_config_message[{:d}] = {:s}'.format( str_method, len(str_config_message), str(str_config_message))) netconf_handler.edit_config( # configure the device config=str_config_message, target=target, default_operation=default_operation, test_option=test_option, error_option=error_option, format=format) if commit_per_rule: netconf_handler.commit() # configuration commit if 'table_connections' in resource_key: time.sleep(5) # CPU usage might exceed critical level after route redistribution, BGP daemon needs time to reload #results[i] = True results.append(True) except Exception as e: # pylint: disable=broad-except str_operation = 'preparing' if target == 'candidate' else ('deleting' if delete else 'setting') msg = '[{:s}] Exception {:s} {:s}: {:s}' logger.exception(msg.format(str_method, str_operation, str_resource_name, str(resource))) #results[i] = e # if validation fails, store the exception results.append(e) if not commit_per_rule: try: netconf_handler.commit() except Exception as e: # pylint: disable=broad-except msg = '[{:s}] Exception committing: {:s}' str_operation = 'preparing' if target == 'candidate' else ('deleting' if delete else 'setting') logger.exception(msg.format(str_method, str_operation, str(resources))) results = [e for _ in resources] # if commit fails, set exception in each resource return results class OCDriver(_Driver): Loading src/device/service/drivers/oc_driver/templates/IP_LINK/IP_LINK_multivendor.py 0 → 100755 +59 −0 Original line number Diff line number Diff line # 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 src/device/service/drivers/oc_driver/templates/Tools.py +6 −10 Original line number Diff line number Diff line Loading @@ -13,12 +13,10 @@ # limitations under the License. import re,logging import json import lxml.etree as ET from typing import Collection, Dict, Any from typing import Collection, Dict from .IP_LINK.IP_LINK_multivendor import ip_link_mgmt from yattag import Doc, indent from .VPN.physical import create_optical_channel def add_value_from_tag(target : Dict, field_name: str, field_value : ET.Element, cast=None) -> None: if isinstance(field_value,str) or field_value is None or field_value.text is None: return Loading Loading @@ -49,14 +47,12 @@ def add_value_from_collection(target : Dict, field_name: str, field_value : Coll # Return: [dict] Set of templates generated according to the configuration rule """ def generate_templates(resource_key: str, resource_value: str, channel:str) -> str: # template management to be configured def generate_templates(resource_key: str, resource_value: str, delete: bool,vendor:str) -> str: # template management to be configured result_templates = [] data={} data['name']=channel data['resource_key']=resource_key data['value']=resource_value #result_templates.append(create_physical_config(data)) list_resource_key = resource_key.split("/") # the rule resource key management if "ip_link" in list_resource_key[1]: # network instance rules management result_templates.append(ip_link_mgmt(resource_value,vendor,delete)) return result_templates Loading src/device/service/drivers/oc_driver/templates/__init__.py +285 −0 Original line number Diff line number Diff line Loading @@ -12,3 +12,288 @@ # See the License for the specific language governing permissions and # limitations under the License. import json, logging, lxml.etree as ET, re import time from typing import Any, Dict, Optional from jinja2 import Environment, PackageLoader, select_autoescape import paramiko from .Tools import generate_templates LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__) RE_REMOVE_FILTERS = re.compile(r'\[[^\]]+\]') RE_REMOVE_FILTERS_2 = re.compile(r'\/[a-z]+:') EMPTY_CONFIG = '<config></config>' EMPTY_FILTER = '<filter></filter>' JINJA_ENV = Environment(loader=PackageLoader('device.service.drivers.openconfig'), autoescape=select_autoescape()) """ # Method Name: compose_config # Parameters: - resource_key: [str] Variable to identify the rule to be executed. - resource_value: [str] Variable with the configuration parameters of the rule to be executed. - delete: [bool] Variable to identify whether to create or delete the rule. - vendor: [str] Variable to identify the vendor of the equipment to be configured. - message_renderer [str] Variable to dientify template generation method. Can be "jinja" or "pyangbind". # Functionality: This method calls the function obtains the equipment configuration template according to the value of the variable "message_renderer". Depending on the value of this variable, it gets the template with "jinja" or "pyangbind". # Return: [dict] Set of templates obtained according to the configuration method """ def compose_config( # template generation resource_key : str, resource_value : str, delete : bool = False, vendor : Optional[str] = None, message_renderer = str ) -> str: if (message_renderer == "pyangbind"): templates = (generate_templates(resource_key, resource_value, delete, vendor)) return [ '<config>{:s}</config>'.format(template) # format correction for template in templates ] elif (message_renderer == "jinja"): templates = [] template_name = '{:s}/edit_config.xml'.format(RE_REMOVE_FILTERS.sub('', resource_key)) templates.append(JINJA_ENV.get_template(template_name)) data : Dict[str, Any] = json.loads(resource_value) operation = 'delete' if delete else 'merge' # others #operation = 'delete' if delete else '' # ipinfusion? return [ '<config>{:s}</config>'.format( template.render(**data, operation=operation, vendor=vendor).strip()) for template in templates ] else: raise ValueError('Invalid message_renderer value: {}'.format(message_renderer)) """ # Method Name: cli_compose_config # Parameters: - resource_key: [str] Variable to identify the rule to be executed. - resource_value: [str] Variable with the configuration parameters of the rule to be executed. - delete: [bool] Variable to identify whether to create or delete the rule. - vendor: [str] Variable to identify the vendor of the equipment to be configured. - message_renderer [str] Variable to dientify template generation method. Can be "jinja" or "pyangbind". # Functionality: This method calls the function obtains the equipment configuration template according to the value of the variable "message_renderer". Depending on the value of this variable, it gets the template with "jinja" or "pyangbind". # Return: [dict] Set of templates obtained according to the configuration method """ def cli_compose_config(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 for path, json_str in resources: data = json.loads(json_str) if 'VC_ID' in data: vc_id = data['VC_ID'] if 'connection_point' in data: connection_point = data['connection_point'] if 'remote_system' in data: remote_system = data['remote_system'] if 'interface' in data: interface = data['interface'] interface = interface.split("-") #New Line To Avoid Bad Endpoint Name In CISCO interface = interface[1] if 'vlan_id' in data: vlan_id = data['vlan_id'] if 'name' in data: ni_name = data['name'] if 'type' in data: ni_type = data['type'] if 'index' in data: subif_index = data['index'] if 'description' in data: description = data['description'] else: description = " " # 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=host, username=user, password=passw, 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() try: # Open an SSH shell channel = ssh_client.invoke_shell() channel.send('enable\n') time.sleep(1) channel.send('conf term\n') time.sleep(0.1) channel.send(f"interface {interface} l2transport\n") time.sleep(0.1) channel.send('description l2vpn_vpws_example\n') time.sleep(0.1) channel.send(f"encapsulation dot1q {vlan_id}\n") time.sleep(0.1) channel.send('mtu 9088\n') time.sleep(0.1) channel.send('commit\n') time.sleep(0.1) channel.send('l2vpn\n') time.sleep(0.1) channel.send('load-balancing flow src-dst-ip\n') time.sleep(0.1) channel.send('pw-class l2vpn_vpws_profile_example\n') time.sleep(0.1) channel.send('encapsulation mpls\n') time.sleep(0.1) channel.send('transport-mode vlan passthrough\n') time.sleep(0.1) channel.send('control-word\n') time.sleep(0.1) channel.send('exit\n') time.sleep(0.1) channel.send('l2vpn\n') time.sleep(0.1) channel.send('xconnect group l2vpn_vpws_group_example\n') time.sleep(0.1) channel.send(f"p2p {ni_name}\n") time.sleep(0.1) channel.send(f"interface {interface}\n") #Ignore the VlanID because the interface already includes the vlanid tag time.sleep(0.1) channel.send(f"neighbor ipv4 {remote_system} pw-id {vc_id}\n") time.sleep(0.1) channel.send('pw-class l2vpn_vpws_profile_example\n') time.sleep(0.1) channel.send('exit\n') time.sleep(0.1) channel.send(f"description {description}\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() def ufi_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 # 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=host, username=user, password=passw, look_for_keys=False) LOGGER.warning("Connection successful") except: 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) output = channel.recv(65535).decode('utf-8') 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 # 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=host, username=user, password=passw, look_for_keys=False) LOGGER.warning("Connection successful") except: 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) output = channel.recv(65535).decode('utf-8') 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() src/service/service/service_handlers/__init__.py +6 −0 Original line number Diff line number Diff line Loading @@ -115,6 +115,12 @@ SERVICE_HANDLERS = [ FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG, } ]), (IP_LinkServiceHandler, [ { FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_IPLINK, FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_OC, } ]), (QKDServiceHandler, [ { FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_QKD, Loading Loading
src/device/service/drivers/oc_driver/OCDriver.py +64 −10 Original line number Diff line number Diff line Loading @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. import time import json import logging, pytz, queue, re, threading #import lxml.etree as ET import logging, pytz, re, threading from typing import Any, List, Tuple, Union from apscheduler.executors.pool import ThreadPoolExecutor from apscheduler.jobstores.memory import MemoryJobStore Loading @@ -22,12 +22,11 @@ from apscheduler.schedulers.background import BackgroundScheduler from ncclient.manager import Manager, connect_ssh from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.tools.client.RetryDecorator import delay_exponential from common.type_checkers.Checkers import chk_type from common.type_checkers.Checkers import chk_length, chk_string, chk_type from device.service.driver_api.Exceptions import UnsupportedResourceKeyException from device.service.driver_api._Driver import _Driver from device.service.driver_api.AnyTreeTools import TreeNode from .templates import compose_config, cli_compose_config, ufi_interface, cisco_interface from .templates.VPN.common import seperate_port_config #from .Tools import xml_pretty_print, xml_to_dict, xml_to_file from .templates.VPN.roadms import ( create_optical_band, disable_media_channel, delete_optical_band, create_media_channel_v2 ) Loading @@ -36,11 +35,10 @@ from .RetryDecorator import retry from context.client.ContextClient import ContextClient from common.proto.context_pb2 import OpticalConfig from .templates.discovery_tool.transponders import transponder_values_extractor from .templates.discovery_tool.roadms import roadm_values_extractor, extract_media_channels from .templates.discovery_tool.roadms import roadm_values_extractor from .templates.discovery_tool.open_roadm import openroadm_values_extractor from .templates.VPN.openroadm import network_media_channel_handler DEBUG_MODE = False logging.getLogger('ncclient.manager').setLevel(logging.DEBUG if DEBUG_MODE else logging.WARNING) logging.getLogger('ncclient.transport.ssh').setLevel(logging.DEBUG if DEBUG_MODE else logging.WARNING) Loading @@ -51,9 +49,6 @@ logging.getLogger('monitoring-client').setLevel(logging.INFO if DEBUG_MODE else RE_GET_ENDPOINT_FROM_INTERFACE_KEY = re.compile(r'.*interface\[([^\]]+)\].*') RE_GET_ENDPOINT_FROM_INTERFACE_XPATH = re.compile(r".*interface\[oci\:name\='([^\]]+)'\].*") # Collection of samples through NetConf is very slow and each request collects all the data. # Populate a cache periodically (when first interface is interrogated). # Evict data after some seconds, when data is considered as outdated SAMPLE_EVICTION_SECONDS = 30.0 # seconds SAMPLE_RESOURCE_KEY = 'interfaces/interface/state/counters' Loading Loading @@ -201,6 +196,65 @@ def edit_config( #results[i] = True results.append(result) 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: {:s}".format(resources)) ufi_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) else: for i,resource in enumerate(resources): str_resource_name = 'resources[#{:d}]'.format(i) try: logger.debug('[{:s}] resource = {:s}'.format(str_method, str(resource))) chk_type(str_resource_name, resource, (list, tuple)) chk_length(str_resource_name, resource, min_length=2, max_length=2) resource_key,resource_value = resource chk_string(str_resource_name + '.key', resource_key, allow_empty=False) str_config_messages = compose_config( # get template for configuration resource_key, resource_value, delete=delete, vendor=netconf_handler.vendor, message_renderer=netconf_handler.message_renderer) for str_config_message in str_config_messages: # configuration of the received templates if str_config_message is None: raise UnsupportedResourceKeyException(resource_key) logger.debug('[{:s}] str_config_message[{:d}] = {:s}'.format( str_method, len(str_config_message), str(str_config_message))) netconf_handler.edit_config( # configure the device config=str_config_message, target=target, default_operation=default_operation, test_option=test_option, error_option=error_option, format=format) if commit_per_rule: netconf_handler.commit() # configuration commit if 'table_connections' in resource_key: time.sleep(5) # CPU usage might exceed critical level after route redistribution, BGP daemon needs time to reload #results[i] = True results.append(True) except Exception as e: # pylint: disable=broad-except str_operation = 'preparing' if target == 'candidate' else ('deleting' if delete else 'setting') msg = '[{:s}] Exception {:s} {:s}: {:s}' logger.exception(msg.format(str_method, str_operation, str_resource_name, str(resource))) #results[i] = e # if validation fails, store the exception results.append(e) if not commit_per_rule: try: netconf_handler.commit() except Exception as e: # pylint: disable=broad-except msg = '[{:s}] Exception committing: {:s}' str_operation = 'preparing' if target == 'candidate' else ('deleting' if delete else 'setting') logger.exception(msg.format(str_method, str_operation, str(resources))) results = [e for _ in resources] # if commit fails, set exception in each resource return results class OCDriver(_Driver): Loading
src/device/service/drivers/oc_driver/templates/IP_LINK/IP_LINK_multivendor.py 0 → 100755 +59 −0 Original line number Diff line number Diff line # 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
src/device/service/drivers/oc_driver/templates/Tools.py +6 −10 Original line number Diff line number Diff line Loading @@ -13,12 +13,10 @@ # limitations under the License. import re,logging import json import lxml.etree as ET from typing import Collection, Dict, Any from typing import Collection, Dict from .IP_LINK.IP_LINK_multivendor import ip_link_mgmt from yattag import Doc, indent from .VPN.physical import create_optical_channel def add_value_from_tag(target : Dict, field_name: str, field_value : ET.Element, cast=None) -> None: if isinstance(field_value,str) or field_value is None or field_value.text is None: return Loading Loading @@ -49,14 +47,12 @@ def add_value_from_collection(target : Dict, field_name: str, field_value : Coll # Return: [dict] Set of templates generated according to the configuration rule """ def generate_templates(resource_key: str, resource_value: str, channel:str) -> str: # template management to be configured def generate_templates(resource_key: str, resource_value: str, delete: bool,vendor:str) -> str: # template management to be configured result_templates = [] data={} data['name']=channel data['resource_key']=resource_key data['value']=resource_value #result_templates.append(create_physical_config(data)) list_resource_key = resource_key.split("/") # the rule resource key management if "ip_link" in list_resource_key[1]: # network instance rules management result_templates.append(ip_link_mgmt(resource_value,vendor,delete)) return result_templates Loading
src/device/service/drivers/oc_driver/templates/__init__.py +285 −0 Original line number Diff line number Diff line Loading @@ -12,3 +12,288 @@ # See the License for the specific language governing permissions and # limitations under the License. import json, logging, lxml.etree as ET, re import time from typing import Any, Dict, Optional from jinja2 import Environment, PackageLoader, select_autoescape import paramiko from .Tools import generate_templates LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__) RE_REMOVE_FILTERS = re.compile(r'\[[^\]]+\]') RE_REMOVE_FILTERS_2 = re.compile(r'\/[a-z]+:') EMPTY_CONFIG = '<config></config>' EMPTY_FILTER = '<filter></filter>' JINJA_ENV = Environment(loader=PackageLoader('device.service.drivers.openconfig'), autoescape=select_autoescape()) """ # Method Name: compose_config # Parameters: - resource_key: [str] Variable to identify the rule to be executed. - resource_value: [str] Variable with the configuration parameters of the rule to be executed. - delete: [bool] Variable to identify whether to create or delete the rule. - vendor: [str] Variable to identify the vendor of the equipment to be configured. - message_renderer [str] Variable to dientify template generation method. Can be "jinja" or "pyangbind". # Functionality: This method calls the function obtains the equipment configuration template according to the value of the variable "message_renderer". Depending on the value of this variable, it gets the template with "jinja" or "pyangbind". # Return: [dict] Set of templates obtained according to the configuration method """ def compose_config( # template generation resource_key : str, resource_value : str, delete : bool = False, vendor : Optional[str] = None, message_renderer = str ) -> str: if (message_renderer == "pyangbind"): templates = (generate_templates(resource_key, resource_value, delete, vendor)) return [ '<config>{:s}</config>'.format(template) # format correction for template in templates ] elif (message_renderer == "jinja"): templates = [] template_name = '{:s}/edit_config.xml'.format(RE_REMOVE_FILTERS.sub('', resource_key)) templates.append(JINJA_ENV.get_template(template_name)) data : Dict[str, Any] = json.loads(resource_value) operation = 'delete' if delete else 'merge' # others #operation = 'delete' if delete else '' # ipinfusion? return [ '<config>{:s}</config>'.format( template.render(**data, operation=operation, vendor=vendor).strip()) for template in templates ] else: raise ValueError('Invalid message_renderer value: {}'.format(message_renderer)) """ # Method Name: cli_compose_config # Parameters: - resource_key: [str] Variable to identify the rule to be executed. - resource_value: [str] Variable with the configuration parameters of the rule to be executed. - delete: [bool] Variable to identify whether to create or delete the rule. - vendor: [str] Variable to identify the vendor of the equipment to be configured. - message_renderer [str] Variable to dientify template generation method. Can be "jinja" or "pyangbind". # Functionality: This method calls the function obtains the equipment configuration template according to the value of the variable "message_renderer". Depending on the value of this variable, it gets the template with "jinja" or "pyangbind". # Return: [dict] Set of templates obtained according to the configuration method """ def cli_compose_config(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 for path, json_str in resources: data = json.loads(json_str) if 'VC_ID' in data: vc_id = data['VC_ID'] if 'connection_point' in data: connection_point = data['connection_point'] if 'remote_system' in data: remote_system = data['remote_system'] if 'interface' in data: interface = data['interface'] interface = interface.split("-") #New Line To Avoid Bad Endpoint Name In CISCO interface = interface[1] if 'vlan_id' in data: vlan_id = data['vlan_id'] if 'name' in data: ni_name = data['name'] if 'type' in data: ni_type = data['type'] if 'index' in data: subif_index = data['index'] if 'description' in data: description = data['description'] else: description = " " # 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=host, username=user, password=passw, 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() try: # Open an SSH shell channel = ssh_client.invoke_shell() channel.send('enable\n') time.sleep(1) channel.send('conf term\n') time.sleep(0.1) channel.send(f"interface {interface} l2transport\n") time.sleep(0.1) channel.send('description l2vpn_vpws_example\n') time.sleep(0.1) channel.send(f"encapsulation dot1q {vlan_id}\n") time.sleep(0.1) channel.send('mtu 9088\n') time.sleep(0.1) channel.send('commit\n') time.sleep(0.1) channel.send('l2vpn\n') time.sleep(0.1) channel.send('load-balancing flow src-dst-ip\n') time.sleep(0.1) channel.send('pw-class l2vpn_vpws_profile_example\n') time.sleep(0.1) channel.send('encapsulation mpls\n') time.sleep(0.1) channel.send('transport-mode vlan passthrough\n') time.sleep(0.1) channel.send('control-word\n') time.sleep(0.1) channel.send('exit\n') time.sleep(0.1) channel.send('l2vpn\n') time.sleep(0.1) channel.send('xconnect group l2vpn_vpws_group_example\n') time.sleep(0.1) channel.send(f"p2p {ni_name}\n") time.sleep(0.1) channel.send(f"interface {interface}\n") #Ignore the VlanID because the interface already includes the vlanid tag time.sleep(0.1) channel.send(f"neighbor ipv4 {remote_system} pw-id {vc_id}\n") time.sleep(0.1) channel.send('pw-class l2vpn_vpws_profile_example\n') time.sleep(0.1) channel.send('exit\n') time.sleep(0.1) channel.send(f"description {description}\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() def ufi_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 # 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=host, username=user, password=passw, look_for_keys=False) LOGGER.warning("Connection successful") except: 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) output = channel.recv(65535).decode('utf-8') 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 # 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=host, username=user, password=passw, look_for_keys=False) LOGGER.warning("Connection successful") except: 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) output = channel.recv(65535).decode('utf-8') 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()
src/service/service/service_handlers/__init__.py +6 −0 Original line number Diff line number Diff line Loading @@ -115,6 +115,12 @@ SERVICE_HANDLERS = [ FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG, } ]), (IP_LinkServiceHandler, [ { FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_IPLINK, FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_OC, } ]), (QKDServiceHandler, [ { FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_QKD, Loading