Loading src/device/service/drivers/openconfig/OpenConfigDriver.py +33 −15 Original line number Diff line number Diff line Loading @@ -61,11 +61,13 @@ class NetconfSessionHandler: self.__port = int(port) self.__username = settings.get('username') self.__password = settings.get('password') self.__vendor = settings.get('vendor') self.__key_filename = settings.get('key_filename') self.__hostkey_verify = settings.get('hostkey_verify', True) self.__look_for_keys = settings.get('look_for_keys', True) self.__allow_agent = settings.get('allow_agent', True) self.__force_running = settings.get('force_running', False) self.__commit_per_delete = settings.get('delete_rule', False) self.__device_params = settings.get('device_params', {}) self.__manager_params = settings.get('manager_params', {}) self.__nc_params = settings.get('nc_params', {}) Loading @@ -90,6 +92,12 @@ class NetconfSessionHandler: @property def use_candidate(self): return self.__candidate_supported and not self.__force_running @property def commit_per_rule(self): return self.__commit_per_delete @property def vendor(self): return self.__vendor @RETRY_DECORATOR def get(self, filter=None, with_defaults=None): # pylint: disable=redefined-builtin with self.__lock: Loading Loading @@ -181,8 +189,9 @@ def do_sampling(samples_cache : SamplesCache, resource_key : str, out_samples : LOGGER.exception('Error retrieving samples') def edit_config( netconf_handler : NetconfSessionHandler, resources : List[Tuple[str, Any]], delete=False, target='running', default_operation='merge', test_option=None, error_option=None, format='xml' # pylint: disable=redefined-builtin netconf_handler : NetconfSessionHandler, resources : List[Tuple[str, Any]], delete=False, commit_per_rule= False, target='running', default_operation='merge', test_option=None, error_option=None, format='xml' # pylint: disable=redefined-builtin ): str_method = 'DeleteConfig' if delete else 'SetConfig' LOGGER.info('[{:s}] resources = {:s}'.format(str_method, str(resources))) Loading @@ -195,13 +204,16 @@ def edit_config( 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_message = compose_config(resource_key, resource_value, delete=delete) str_config_message = compose_config( resource_key, resource_value, delete=delete, vendor=netconf_handler.vendor) if str_config_message is None: raise UnsupportedResourceKeyException(resource_key) LOGGER.info('[{:s}] str_config_message[{:d}] = {:s}'.format( str_method, len(str_config_message), str(str_config_message))) netconf_handler.edit_config( 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() results[i] = True except Exception as e: # pylint: disable=broad-except str_operation = 'preparing' if target == 'candidate' else ('deleting' if delete else 'setting') Loading Loading @@ -278,6 +290,9 @@ class OpenConfigDriver(_Driver): with self.__lock: if self.__netconf_handler.use_candidate: with self.__netconf_handler.locked(target='candidate'): if self.__netconf_handler.commit_per_rule: results = edit_config(self.__netconf_handler, resources, target='candidate', commit_per_rule= True) else: results = edit_config(self.__netconf_handler, resources, target='candidate') try: self.__netconf_handler.commit() Loading @@ -294,6 +309,9 @@ class OpenConfigDriver(_Driver): with self.__lock: if self.__netconf_handler.use_candidate: with self.__netconf_handler.locked(target='candidate'): if self.__netconf_handler.commit_per_rule: results = edit_config(self.__netconf_handler, resources, target='candidate', delete=True, commit_per_rule= True) else: results = edit_config(self.__netconf_handler, resources, target='candidate', delete=True) try: self.__netconf_handler.commit() Loading src/device/service/drivers/openconfig/templates/Interfaces.py +8 −1 Original line number Diff line number Diff line Loading @@ -37,6 +37,10 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: #interface_type = xml_interface.find('oci:config/oci:type', namespaces=NAMESPACES) #add_value_from_tag(interface, 'type', interface_type) interface_type = xml_interface.find('oci:config/oci:type', namespaces=NAMESPACES) interface_type.text = interface_type.text.replace('ianaift:','') add_value_from_tag(interface, 'type', interface_type) interface_mtu = xml_interface.find('oci:config/oci:mtu', namespaces=NAMESPACES) add_value_from_tag(interface, 'mtu', interface_mtu, cast=int) Loading @@ -49,12 +53,15 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: subinterface = {} add_value_from_tag(subinterface, 'name', interface_name) add_value_from_tag(subinterface, 'mtu', interface_mtu) add_value_from_tag(subinterface, 'type', interface_type) subinterface_index = xml_subinterface.find('oci:index', namespaces=NAMESPACES) if subinterface_index is None or subinterface_index.text is None: continue add_value_from_tag(subinterface, 'index', subinterface_index, cast=int) vlan_id = xml_subinterface.find('ocv:vlan/ocv:config/ocv:vlan-id', namespaces=NAMESPACES) vlan_id = xml_subinterface.find('ocv:vlan/ocv:match/ocv:single-tagged/ocv:config/ocv:vlan-id', namespaces=NAMESPACES) add_value_from_tag(subinterface, 'vlan_id', vlan_id, cast=int) # TODO: implement support for multiple IP addresses per subinterface Loading src/device/service/drivers/openconfig/templates/NetworkInstances.py +26 −3 Original line number Diff line number Diff line Loading @@ -27,6 +27,9 @@ XPATH_NI_IIP_AP = ".//ocni:inter-instance-policies/ocni:apply-policy" XPATH_NI_IIP_AP_IMPORT = ".//ocni:config/ocni:import-policy" XPATH_NI_IIP_AP_EXPORT = ".//ocni:config/ocni:export-policy" XPATH_NI_CPOINTS = ".//ocni:connection-points/ocni:connection-point" XPATH_NI_CPOINTS_ENDPOINT = ".//ocni:endpoints/ocni:endpoint/ocni:remote/ocni:config" def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: response = [] for xml_network_instance in xml_data.xpath(XPATH_NETWORK_INSTANCES, namespaces=NAMESPACES): Loading @@ -39,10 +42,11 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: add_value_from_tag(network_instance, 'name', ni_name) ni_type = xml_network_instance.find('ocni:config/ocni:type', namespaces=NAMESPACES) ni_type.text = ni_type.text.replace('oc-ni-types:','') add_value_from_tag(network_instance, 'type', ni_type) #ni_router_id = xml_network_instance.find('ocni:config/ocni:router-id', namespaces=NAMESPACES) #add_value_from_tag(network_instance, 'router_id', ni_router_id) ni_router_id = xml_network_instance.find('ocni:config/ocni:router-id', namespaces=NAMESPACES) add_value_from_tag(network_instance, 'router_id', ni_router_id) ni_route_dist = xml_network_instance.find('ocni:config/ocni:route-distinguisher', namespaces=NAMESPACES) add_value_from_tag(network_instance, 'route_distinguisher', ni_route_dist) Loading @@ -53,6 +57,20 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: if len(network_instance) == 0: continue response.append(('/network_instance[{:s}]'.format(network_instance['name']), network_instance)) for xml_cpoints in xml_network_instance.xpath(XPATH_NI_PROTOCOLS, namespaces=NAMESPACES): cpoint = {} add_value_from_tag(cpoint, 'name', ni_name) connection_point = xml_cpoints.find('ocni:connection-point-id', namespaces=NAMESPACES) add_value_from_tag(cpoint, 'connection_point', connection_point) for xml_endpoint in xml_cpoints.xpath(XPATH_NI_CPOINTS_ENDPOINT, namespaces=NAMESPACES): remote_system = xml_endpoint.find('ocni:remote-system', namespaces=NAMESPACES) add_value_from_tag(cpoint, 'remote_system', remote_system) VC_ID = xml_endpoint.find('ocni:virtual-circuit-identifier', namespaces=NAMESPACES) add_value_from_tag(cpoint, 'VC_ID', VC_ID) for xml_protocol in xml_network_instance.xpath(XPATH_NI_PROTOCOLS, namespaces=NAMESPACES): #LOGGER.info('xml_protocol = {:s}'.format(str(ET.tostring(xml_protocol)))) Loading @@ -71,6 +89,8 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: if protocol['identifier'] == 'BGP': bgp_as = xml_protocol.find('ocni:bgp/ocni:global/ocni:config/ocni:as', namespaces=NAMESPACES) add_value_from_tag(protocol, 'as', bgp_as, cast=int) bgp_id = xml_protocol.find('ocni:bgp/ocni:global/ocni:config/ocni:router-id', namespaces=NAMESPACES) add_value_from_tag(protocol, 'router_id', bgp_id) resource_key = '/network_instance[{:s}]/protocols[{:s}]'.format( network_instance['name'], protocol['identifier']) Loading @@ -94,7 +114,7 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: add_value_from_tag(table_connection, 'address_family', address_family, cast=lambda s: s.replace('oc-types:', '')) default_import_policy = xml_table_connection.find('ocni:default-import-policy', namespaces=NAMESPACES) default_import_policy = xml_table_connection.find('ocni:config/ocni:default-import-policy', namespaces=NAMESPACES) add_value_from_tag(table_connection, 'default_import_policy', default_import_policy) resource_key = '/network_instance[{:s}]/table_connections[{:s}][{:s}][{:s}]'.format( Loading Loading @@ -125,4 +145,7 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: iip_ap['name'], iip_ap['export_policy']) response.append((resource_key, iip_ap)) return response src/device/service/drivers/openconfig/templates/RoutingPolicy.py +1 −1 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: resource_key = '/routing_policy/bgp_defined_set[{:s}]'.format(bgp_ext_community_set['ext_community_set_name']) response.append((resource_key, copy.deepcopy(bgp_ext_community_set))) ext_community_member = xml_bgp_ext_community_set.find('ocbp:ext-community-member', namespaces=NAMESPACES) ext_community_member = xml_bgp_ext_community_set.find('ocbp:config/ocbp:ext-community-member', namespaces=NAMESPACES) if ext_community_member is not None and ext_community_member.text is not None: add_value_from_tag(bgp_ext_community_set, 'ext_community_member', ext_community_member) Loading src/device/service/drivers/openconfig/templates/__init__.py +5 −3 Original line number Diff line number Diff line Loading @@ -13,7 +13,7 @@ # limitations under the License. import json, logging, lxml.etree as ET, re from typing import Any, Dict from typing import Any, Dict, Optional from jinja2 import Environment, PackageLoader, select_autoescape from device.service.driver_api._Driver import ( RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES, RESOURCE_ROUTING_POLICIES, RESOURCE_ACL) Loading Loading @@ -77,9 +77,11 @@ def parse(resource_key : str, xml_data : ET.Element): if parser is None: return [(resource_key, xml_data)] return parser(xml_data) def compose_config(resource_key : str, resource_value : str, delete : bool = False) -> str: def compose_config( resource_key : str, resource_value : str, delete : bool = False, vendor : Optional[str] = None ) -> str: template_name = '{:s}/edit_config.xml'.format(RE_REMOVE_FILTERS.sub('', resource_key)) template = JINJA_ENV.get_template(template_name) data : Dict[str, Any] = json.loads(resource_value) operation = 'delete' if delete else 'merge' return '<config>{:s}</config>'.format(template.render(**data, operation=operation).strip()) return '<config>{:s}</config>'.format(template.render(**data, operation=operation, vendor=vendor).strip()) Loading
src/device/service/drivers/openconfig/OpenConfigDriver.py +33 −15 Original line number Diff line number Diff line Loading @@ -61,11 +61,13 @@ class NetconfSessionHandler: self.__port = int(port) self.__username = settings.get('username') self.__password = settings.get('password') self.__vendor = settings.get('vendor') self.__key_filename = settings.get('key_filename') self.__hostkey_verify = settings.get('hostkey_verify', True) self.__look_for_keys = settings.get('look_for_keys', True) self.__allow_agent = settings.get('allow_agent', True) self.__force_running = settings.get('force_running', False) self.__commit_per_delete = settings.get('delete_rule', False) self.__device_params = settings.get('device_params', {}) self.__manager_params = settings.get('manager_params', {}) self.__nc_params = settings.get('nc_params', {}) Loading @@ -90,6 +92,12 @@ class NetconfSessionHandler: @property def use_candidate(self): return self.__candidate_supported and not self.__force_running @property def commit_per_rule(self): return self.__commit_per_delete @property def vendor(self): return self.__vendor @RETRY_DECORATOR def get(self, filter=None, with_defaults=None): # pylint: disable=redefined-builtin with self.__lock: Loading Loading @@ -181,8 +189,9 @@ def do_sampling(samples_cache : SamplesCache, resource_key : str, out_samples : LOGGER.exception('Error retrieving samples') def edit_config( netconf_handler : NetconfSessionHandler, resources : List[Tuple[str, Any]], delete=False, target='running', default_operation='merge', test_option=None, error_option=None, format='xml' # pylint: disable=redefined-builtin netconf_handler : NetconfSessionHandler, resources : List[Tuple[str, Any]], delete=False, commit_per_rule= False, target='running', default_operation='merge', test_option=None, error_option=None, format='xml' # pylint: disable=redefined-builtin ): str_method = 'DeleteConfig' if delete else 'SetConfig' LOGGER.info('[{:s}] resources = {:s}'.format(str_method, str(resources))) Loading @@ -195,13 +204,16 @@ def edit_config( 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_message = compose_config(resource_key, resource_value, delete=delete) str_config_message = compose_config( resource_key, resource_value, delete=delete, vendor=netconf_handler.vendor) if str_config_message is None: raise UnsupportedResourceKeyException(resource_key) LOGGER.info('[{:s}] str_config_message[{:d}] = {:s}'.format( str_method, len(str_config_message), str(str_config_message))) netconf_handler.edit_config( 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() results[i] = True except Exception as e: # pylint: disable=broad-except str_operation = 'preparing' if target == 'candidate' else ('deleting' if delete else 'setting') Loading Loading @@ -278,6 +290,9 @@ class OpenConfigDriver(_Driver): with self.__lock: if self.__netconf_handler.use_candidate: with self.__netconf_handler.locked(target='candidate'): if self.__netconf_handler.commit_per_rule: results = edit_config(self.__netconf_handler, resources, target='candidate', commit_per_rule= True) else: results = edit_config(self.__netconf_handler, resources, target='candidate') try: self.__netconf_handler.commit() Loading @@ -294,6 +309,9 @@ class OpenConfigDriver(_Driver): with self.__lock: if self.__netconf_handler.use_candidate: with self.__netconf_handler.locked(target='candidate'): if self.__netconf_handler.commit_per_rule: results = edit_config(self.__netconf_handler, resources, target='candidate', delete=True, commit_per_rule= True) else: results = edit_config(self.__netconf_handler, resources, target='candidate', delete=True) try: self.__netconf_handler.commit() Loading
src/device/service/drivers/openconfig/templates/Interfaces.py +8 −1 Original line number Diff line number Diff line Loading @@ -37,6 +37,10 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: #interface_type = xml_interface.find('oci:config/oci:type', namespaces=NAMESPACES) #add_value_from_tag(interface, 'type', interface_type) interface_type = xml_interface.find('oci:config/oci:type', namespaces=NAMESPACES) interface_type.text = interface_type.text.replace('ianaift:','') add_value_from_tag(interface, 'type', interface_type) interface_mtu = xml_interface.find('oci:config/oci:mtu', namespaces=NAMESPACES) add_value_from_tag(interface, 'mtu', interface_mtu, cast=int) Loading @@ -49,12 +53,15 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: subinterface = {} add_value_from_tag(subinterface, 'name', interface_name) add_value_from_tag(subinterface, 'mtu', interface_mtu) add_value_from_tag(subinterface, 'type', interface_type) subinterface_index = xml_subinterface.find('oci:index', namespaces=NAMESPACES) if subinterface_index is None or subinterface_index.text is None: continue add_value_from_tag(subinterface, 'index', subinterface_index, cast=int) vlan_id = xml_subinterface.find('ocv:vlan/ocv:config/ocv:vlan-id', namespaces=NAMESPACES) vlan_id = xml_subinterface.find('ocv:vlan/ocv:match/ocv:single-tagged/ocv:config/ocv:vlan-id', namespaces=NAMESPACES) add_value_from_tag(subinterface, 'vlan_id', vlan_id, cast=int) # TODO: implement support for multiple IP addresses per subinterface Loading
src/device/service/drivers/openconfig/templates/NetworkInstances.py +26 −3 Original line number Diff line number Diff line Loading @@ -27,6 +27,9 @@ XPATH_NI_IIP_AP = ".//ocni:inter-instance-policies/ocni:apply-policy" XPATH_NI_IIP_AP_IMPORT = ".//ocni:config/ocni:import-policy" XPATH_NI_IIP_AP_EXPORT = ".//ocni:config/ocni:export-policy" XPATH_NI_CPOINTS = ".//ocni:connection-points/ocni:connection-point" XPATH_NI_CPOINTS_ENDPOINT = ".//ocni:endpoints/ocni:endpoint/ocni:remote/ocni:config" def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: response = [] for xml_network_instance in xml_data.xpath(XPATH_NETWORK_INSTANCES, namespaces=NAMESPACES): Loading @@ -39,10 +42,11 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: add_value_from_tag(network_instance, 'name', ni_name) ni_type = xml_network_instance.find('ocni:config/ocni:type', namespaces=NAMESPACES) ni_type.text = ni_type.text.replace('oc-ni-types:','') add_value_from_tag(network_instance, 'type', ni_type) #ni_router_id = xml_network_instance.find('ocni:config/ocni:router-id', namespaces=NAMESPACES) #add_value_from_tag(network_instance, 'router_id', ni_router_id) ni_router_id = xml_network_instance.find('ocni:config/ocni:router-id', namespaces=NAMESPACES) add_value_from_tag(network_instance, 'router_id', ni_router_id) ni_route_dist = xml_network_instance.find('ocni:config/ocni:route-distinguisher', namespaces=NAMESPACES) add_value_from_tag(network_instance, 'route_distinguisher', ni_route_dist) Loading @@ -53,6 +57,20 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: if len(network_instance) == 0: continue response.append(('/network_instance[{:s}]'.format(network_instance['name']), network_instance)) for xml_cpoints in xml_network_instance.xpath(XPATH_NI_PROTOCOLS, namespaces=NAMESPACES): cpoint = {} add_value_from_tag(cpoint, 'name', ni_name) connection_point = xml_cpoints.find('ocni:connection-point-id', namespaces=NAMESPACES) add_value_from_tag(cpoint, 'connection_point', connection_point) for xml_endpoint in xml_cpoints.xpath(XPATH_NI_CPOINTS_ENDPOINT, namespaces=NAMESPACES): remote_system = xml_endpoint.find('ocni:remote-system', namespaces=NAMESPACES) add_value_from_tag(cpoint, 'remote_system', remote_system) VC_ID = xml_endpoint.find('ocni:virtual-circuit-identifier', namespaces=NAMESPACES) add_value_from_tag(cpoint, 'VC_ID', VC_ID) for xml_protocol in xml_network_instance.xpath(XPATH_NI_PROTOCOLS, namespaces=NAMESPACES): #LOGGER.info('xml_protocol = {:s}'.format(str(ET.tostring(xml_protocol)))) Loading @@ -71,6 +89,8 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: if protocol['identifier'] == 'BGP': bgp_as = xml_protocol.find('ocni:bgp/ocni:global/ocni:config/ocni:as', namespaces=NAMESPACES) add_value_from_tag(protocol, 'as', bgp_as, cast=int) bgp_id = xml_protocol.find('ocni:bgp/ocni:global/ocni:config/ocni:router-id', namespaces=NAMESPACES) add_value_from_tag(protocol, 'router_id', bgp_id) resource_key = '/network_instance[{:s}]/protocols[{:s}]'.format( network_instance['name'], protocol['identifier']) Loading @@ -94,7 +114,7 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: add_value_from_tag(table_connection, 'address_family', address_family, cast=lambda s: s.replace('oc-types:', '')) default_import_policy = xml_table_connection.find('ocni:default-import-policy', namespaces=NAMESPACES) default_import_policy = xml_table_connection.find('ocni:config/ocni:default-import-policy', namespaces=NAMESPACES) add_value_from_tag(table_connection, 'default_import_policy', default_import_policy) resource_key = '/network_instance[{:s}]/table_connections[{:s}][{:s}][{:s}]'.format( Loading Loading @@ -125,4 +145,7 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: iip_ap['name'], iip_ap['export_policy']) response.append((resource_key, iip_ap)) return response
src/device/service/drivers/openconfig/templates/RoutingPolicy.py +1 −1 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: resource_key = '/routing_policy/bgp_defined_set[{:s}]'.format(bgp_ext_community_set['ext_community_set_name']) response.append((resource_key, copy.deepcopy(bgp_ext_community_set))) ext_community_member = xml_bgp_ext_community_set.find('ocbp:ext-community-member', namespaces=NAMESPACES) ext_community_member = xml_bgp_ext_community_set.find('ocbp:config/ocbp:ext-community-member', namespaces=NAMESPACES) if ext_community_member is not None and ext_community_member.text is not None: add_value_from_tag(bgp_ext_community_set, 'ext_community_member', ext_community_member) Loading
src/device/service/drivers/openconfig/templates/__init__.py +5 −3 Original line number Diff line number Diff line Loading @@ -13,7 +13,7 @@ # limitations under the License. import json, logging, lxml.etree as ET, re from typing import Any, Dict from typing import Any, Dict, Optional from jinja2 import Environment, PackageLoader, select_autoescape from device.service.driver_api._Driver import ( RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES, RESOURCE_ROUTING_POLICIES, RESOURCE_ACL) Loading Loading @@ -77,9 +77,11 @@ def parse(resource_key : str, xml_data : ET.Element): if parser is None: return [(resource_key, xml_data)] return parser(xml_data) def compose_config(resource_key : str, resource_value : str, delete : bool = False) -> str: def compose_config( resource_key : str, resource_value : str, delete : bool = False, vendor : Optional[str] = None ) -> str: template_name = '{:s}/edit_config.xml'.format(RE_REMOVE_FILTERS.sub('', resource_key)) template = JINJA_ENV.get_template(template_name) data : Dict[str, Any] = json.loads(resource_value) operation = 'delete' if delete else 'merge' return '<config>{:s}</config>'.format(template.render(**data, operation=operation).strip()) return '<config>{:s}</config>'.format(template.render(**data, operation=operation, vendor=vendor).strip())