Loading .gitlab-ci.yml +38 −38 Original line number Diff line number Diff line Loading @@ -28,44 +28,44 @@ workflow: # include the individual .gitlab-ci.yml of each micro-service and tests include: #- local: '/manifests/.gitlab-ci.yml' - local: '/src/monitoring/.gitlab-ci.yml' - local: '/src/nbi/.gitlab-ci.yml' - local: '/src/context/.gitlab-ci.yml' - local: '/src/device/.gitlab-ci.yml' - local: '/src/service/.gitlab-ci.yml' - local: '/src/qkd_app/.gitlab-ci.yml' - local: '/src/dbscanserving/.gitlab-ci.yml' - local: '/src/opticalattackmitigator/.gitlab-ci.yml' - local: '/src/opticalattackdetector/.gitlab-ci.yml' - local: '/src/opticalattackmanager/.gitlab-ci.yml' - local: '/src/opticalcontroller/.gitlab-ci.yml' - local: '/src/ztp/.gitlab-ci.yml' - local: '/src/policy/.gitlab-ci.yml' - local: '/src/automation/.gitlab-ci.yml' - local: '/src/forecaster/.gitlab-ci.yml' #- local: '/src/webui/.gitlab-ci.yml' #- local: '/src/l3_distributedattackdetector/.gitlab-ci.yml' #- local: '/src/l3_centralizedattackdetector/.gitlab-ci.yml' #- local: '/src/l3_attackmitigator/.gitlab-ci.yml' - local: '/src/slice/.gitlab-ci.yml' #- local: '/src/interdomain/.gitlab-ci.yml' - local: '/src/pathcomp/.gitlab-ci.yml' #- local: '/src/dlt/.gitlab-ci.yml' - local: '/src/load_generator/.gitlab-ci.yml' - local: '/src/bgpls_speaker/.gitlab-ci.yml' - local: '/src/kpi_manager/.gitlab-ci.yml' - local: '/src/kpi_value_api/.gitlab-ci.yml' #- local: '/src/kpi_value_writer/.gitlab-ci.yml' #- local: '/src/telemetry/.gitlab-ci.yml' - local: '/src/analytics/.gitlab-ci.yml' - local: '/src/qos_profile/.gitlab-ci.yml' - local: '/src/vnt_manager/.gitlab-ci.yml' - local: '/src/e2e_orchestrator/.gitlab-ci.yml' - local: '/src/ztp_server/.gitlab-ci.yml' - local: '/src/osm_client/.gitlab-ci.yml' - local: '/src/simap_connector/.gitlab-ci.yml' - local: '/src/pluggables/.gitlab-ci.yml' # #- local: '/manifests/.gitlab-ci.yml' # - local: '/src/monitoring/.gitlab-ci.yml' # - local: '/src/nbi/.gitlab-ci.yml' # - local: '/src/context/.gitlab-ci.yml' # - local: '/src/device/.gitlab-ci.yml' # - local: '/src/service/.gitlab-ci.yml' # - local: '/src/qkd_app/.gitlab-ci.yml' # - local: '/src/dbscanserving/.gitlab-ci.yml' # - local: '/src/opticalattackmitigator/.gitlab-ci.yml' # - local: '/src/opticalattackdetector/.gitlab-ci.yml' # - local: '/src/opticalattackmanager/.gitlab-ci.yml' # - local: '/src/opticalcontroller/.gitlab-ci.yml' # - local: '/src/ztp/.gitlab-ci.yml' # - local: '/src/policy/.gitlab-ci.yml' # - local: '/src/automation/.gitlab-ci.yml' # - local: '/src/forecaster/.gitlab-ci.yml' # #- local: '/src/webui/.gitlab-ci.yml' # #- local: '/src/l3_distributedattackdetector/.gitlab-ci.yml' # #- local: '/src/l3_centralizedattackdetector/.gitlab-ci.yml' # #- local: '/src/l3_attackmitigator/.gitlab-ci.yml' # - local: '/src/slice/.gitlab-ci.yml' # #- local: '/src/interdomain/.gitlab-ci.yml' # - local: '/src/pathcomp/.gitlab-ci.yml' # #- local: '/src/dlt/.gitlab-ci.yml' # - local: '/src/load_generator/.gitlab-ci.yml' # - local: '/src/bgpls_speaker/.gitlab-ci.yml' # - local: '/src/kpi_manager/.gitlab-ci.yml' # - local: '/src/kpi_value_api/.gitlab-ci.yml' # #- local: '/src/kpi_value_writer/.gitlab-ci.yml' # #- local: '/src/telemetry/.gitlab-ci.yml' # - local: '/src/analytics/.gitlab-ci.yml' # - local: '/src/qos_profile/.gitlab-ci.yml' # - local: '/src/vnt_manager/.gitlab-ci.yml' # - local: '/src/e2e_orchestrator/.gitlab-ci.yml' # - local: '/src/ztp_server/.gitlab-ci.yml' # - local: '/src/osm_client/.gitlab-ci.yml' # - local: '/src/simap_connector/.gitlab-ci.yml' # - local: '/src/pluggables/.gitlab-ci.yml' # This should be last one: end-to-end integration tests - local: '/src/tests/.gitlab-ci.yml' hackfest/mock_osm/__main__.py +1 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ import cmd, logging from .MockOSM import MockOSM logging.basicConfig(level=logging.DEBUG) LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) Loading src/device/service/driver_api/DriverFactory.py +15 −9 Original line number Diff line number Diff line Loading @@ -14,8 +14,7 @@ import logging from enum import Enum from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Type from ._Driver import _Driver from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Set, Tuple, Type from .Exceptions import ( AmbiguousFilterException, EmptyFilterFieldException, UnsatisfiedFilterException, UnsupportedDriverClassException, Loading @@ -23,12 +22,20 @@ from .Exceptions import ( ) from .FilterFields import FILTER_FIELD_ALLOWED_VALUES, FilterFieldEnum if TYPE_CHECKING: from ._Driver import _Driver LOGGER = logging.getLogger(__name__) SUPPORTED_FILTER_FIELDS = set(FILTER_FIELD_ALLOWED_VALUES.keys()) def check_is_class_valid(driver_class : Type['_Driver']) -> None: from ._Driver import _Driver if not issubclass(driver_class, _Driver): raise UnsupportedDriverClassException(str(driver_class)) def sanitize_filter_fields( filter_fields : Dict[FilterFieldEnum, Any], driver_name : Optional[str] = None ) -> Dict[FilterFieldEnum, Any]: Loading Loading @@ -67,14 +74,13 @@ def sanitize_filter_fields( class DriverFactory: def __init__( self, drivers : List[Tuple[Type[_Driver], List[Dict[FilterFieldEnum, Any]]]] self, drivers : List[Tuple[Type['_Driver'], List[Dict[FilterFieldEnum, Any]]]] ) -> None: self.__drivers : List[Tuple[Type[_Driver], Dict[FilterFieldEnum, Any]]] = list() self.__drivers : List[Tuple[Type['_Driver'], Dict[FilterFieldEnum, Any]]] = list() for driver_class,filter_field_sets in drivers: #if not issubclass(driver_class, _Driver): # raise UnsupportedDriverClassException(str(driver_class)) driver_name = driver_class #.__name__ check_is_class_valid(driver_class) driver_name = driver_class.__name__ for filter_fields in filter_field_sets: filter_fields = {k.value:v for k,v in filter_fields.items()} Loading Loading @@ -102,7 +108,7 @@ class DriverFactory: return True def get_driver_class(self, **selection_filter_fields) -> _Driver: def get_driver_class(self, **selection_filter_fields) -> '_Driver': sanitized_filter_fields = sanitize_filter_fields(selection_filter_fields) compatible_drivers : List[Tuple[Type[_Driver], Dict[FilterFieldEnum, Any]]] = [ Loading src/device/service/driver_api/Exceptions.py +12 −10 Original line number Diff line number Diff line Loading @@ -13,22 +13,22 @@ # limitations under the License. class UnsatisfiedFilterException(Exception): def __init__(self, filter_fields): def __init__(self, filter_fields) -> None: msg = 'No Driver satisfies FilterFields({:s})' super().__init__(msg.format(str(filter_fields))) class AmbiguousFilterException(Exception): def __init__(self, filter_fields, compatible_drivers): def __init__(self, filter_fields, compatible_drivers) -> None: msg = 'Multiple Drivers satisfy FilterFields({:s}): {:s}' super().__init__(msg.format(str(filter_fields), str(compatible_drivers))) class UnsupportedDriverClassException(Exception): def __init__(self, driver_class_name): def __init__(self, driver_class_name) -> None: msg = 'Class({:s}) is not a subclass of _Driver' super().__init__(msg.format(str(driver_class_name))) class EmptyFilterFieldException(Exception): def __init__(self, filter_fields, driver_class_name=None): def __init__(self, filter_fields, driver_class_name=None) -> None: if driver_class_name: msg = 'Empty FilterField({:s}) specified by Driver({:s}) is not supported' msg = msg.format(str(filter_fields), str(driver_class_name)) Loading @@ -38,7 +38,7 @@ class EmptyFilterFieldException(Exception): super().__init__(msg) class UnsupportedFilterFieldException(Exception): def __init__(self, unsupported_filter_fields, driver_class_name=None): def __init__(self, unsupported_filter_fields, driver_class_name=None) -> None: if driver_class_name: msg = 'FilterFields({:s}) specified by Driver({:s}) are not supported' msg = msg.format(str(unsupported_filter_fields), str(driver_class_name)) Loading @@ -48,7 +48,9 @@ class UnsupportedFilterFieldException(Exception): super().__init__(msg) class UnsupportedFilterFieldValueException(Exception): def __init__(self, filter_field_name, filter_field_value, allowed_filter_field_values, driver_class_name=None): def __init__( self, filter_field_name, filter_field_value, allowed_filter_field_values, driver_class_name=None ) -> None: if driver_class_name: msg = 'FilterField({:s}={:s}) specified by Driver({:s}) is not supported. Allowed values are {:s}' msg = msg.format( Loading @@ -60,24 +62,24 @@ class UnsupportedFilterFieldValueException(Exception): super().__init__(msg) class DriverInstanceCacheTerminatedException(Exception): def __init__(self): def __init__(self) -> None: msg = 'DriverInstanceCache is terminated. No new instances can be processed.' super().__init__(msg) class UnsupportedResourceKeyException(Exception): def __init__(self, resource_key): def __init__(self, resource_key) -> None: msg = 'ResourceKey({:s}) not supported' msg = msg.format(str(resource_key)) super().__init__(msg) class ConfigFieldNotFoundException(Exception): def __init__(self, config_field_name): def __init__(self, config_field_name) -> None: msg = 'ConfigField({:s}) not specified in resource' msg = msg.format(str(config_field_name)) super().__init__(msg) class ConfigFieldsNotSupportedException(Exception): def __init__(self, config_fields): def __init__(self, config_fields) -> None: msg = 'ConfigFields({:s}) not supported in resource' msg = msg.format(str(config_fields)) super().__init__(msg) src/device/service/drivers/gnmi_openconfig/handlers/InterfaceSwitchedVlan.py 0 → 100644 +104 −0 Original line number Diff line number Diff line # Copyright 2022-2025 ETSI 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, re from typing import Any, Dict, List, Tuple from ._Handler import _Handler from .Tools import get_str from .YangHandler import YangHandler LOGGER = logging.getLogger(__name__) RE_IF_SWITCHED_VLAN = re.compile(r'^/interface\[(?:name=)?([^\]]+)\]/ethernet/switched-vlan$') class InterfaceSwitchedVlanHandler(_Handler): def get_resource_key(self) -> str: return '/interface/ethernet/switched-vlan' def get_path(self) -> str: return '/openconfig-interfaces:interfaces/interface/ethernet/switched-vlan' def _get_interface_name(self, resource_key : str, resource_value : Dict) -> str: if 'name' in resource_value: return get_str(resource_value, 'name') if 'interface' in resource_value: return get_str(resource_value, 'interface') match = RE_IF_SWITCHED_VLAN.match(resource_key) if match is None: MSG = 'Interface name not found in resource_key={:s} resource_value={:s}' raise Exception(MSG.format(str(resource_key), str(resource_value))) return match.groups()[0] def _normalize_config(self, resource_value : Dict) -> Dict[str, Any]: config = resource_value.get('config') if isinstance(config, dict): return config interface_mode = resource_value.get('interface-mode', resource_value.get('interface_mode')) if interface_mode is None: raise Exception('interface-mode is required for switched-vlan config') interface_mode = str(interface_mode).upper() config = {'interface-mode': interface_mode} if interface_mode == 'ACCESS': access_vlan = resource_value.get('access-vlan', resource_value.get('access_vlan')) if access_vlan is None: raise Exception('access-vlan is required for ACCESS mode') config['access-vlan'] = int(access_vlan) elif interface_mode == 'TRUNK': native_vlan = resource_value.get('native-vlan', resource_value.get('native_vlan', 1)) config['native-vlan'] = int(native_vlan) trunk_vlans = resource_value.get('trunk-vlans', resource_value.get('trunk_vlans')) if trunk_vlans is None: trunk_vlan = resource_value.get('trunk-vlan', resource_value.get('trunk_vlan')) trunk_vlans = [trunk_vlan] if trunk_vlan is not None else [] if not isinstance(trunk_vlans, list): trunk_vlans = [trunk_vlans] config['trunk-vlans'] = [int(vlan) for vlan in trunk_vlans if vlan is not None] else: raise Exception('Unsupported interface-mode: {:s}'.format(str(interface_mode))) return config def compose( self, resource_key : str, resource_value : Dict, yang_handler : YangHandler, delete : bool = False ) -> Tuple[str, str]: if_name = self._get_interface_name(resource_key, resource_value) str_path = '/interfaces/interface[name={:s}]/ethernet/switched-vlan'.format(if_name) if delete: return str_path, json.dumps({}) config = self._normalize_config(resource_value) str_data = json.dumps({'config': config}) return str_path, str_data def parse( self, json_data : Dict, yang_handler : YangHandler ) -> List[Tuple[str, Dict[str, Any]]]: json_data_valid = yang_handler.parse_to_dict( '/openconfig-interfaces:interfaces', json_data, fmt='json', strict=False ) entries = [] for interface in json_data_valid.get('interfaces', {}).get('interface', []): interface_name = interface['name'] ethernet = interface.get('ethernet', {}) switched_vlan = ethernet.get('switched-vlan') if switched_vlan is None: continue entry_key = '/interface[{:s}]/ethernet/switched-vlan'.format(interface_name) entry_value = {} if 'config' in switched_vlan: entry_value['config'] = switched_vlan['config'] if 'state' in switched_vlan: entry_value['state'] = switched_vlan['state'] entries.append((entry_key, entry_value)) return entries Loading
.gitlab-ci.yml +38 −38 Original line number Diff line number Diff line Loading @@ -28,44 +28,44 @@ workflow: # include the individual .gitlab-ci.yml of each micro-service and tests include: #- local: '/manifests/.gitlab-ci.yml' - local: '/src/monitoring/.gitlab-ci.yml' - local: '/src/nbi/.gitlab-ci.yml' - local: '/src/context/.gitlab-ci.yml' - local: '/src/device/.gitlab-ci.yml' - local: '/src/service/.gitlab-ci.yml' - local: '/src/qkd_app/.gitlab-ci.yml' - local: '/src/dbscanserving/.gitlab-ci.yml' - local: '/src/opticalattackmitigator/.gitlab-ci.yml' - local: '/src/opticalattackdetector/.gitlab-ci.yml' - local: '/src/opticalattackmanager/.gitlab-ci.yml' - local: '/src/opticalcontroller/.gitlab-ci.yml' - local: '/src/ztp/.gitlab-ci.yml' - local: '/src/policy/.gitlab-ci.yml' - local: '/src/automation/.gitlab-ci.yml' - local: '/src/forecaster/.gitlab-ci.yml' #- local: '/src/webui/.gitlab-ci.yml' #- local: '/src/l3_distributedattackdetector/.gitlab-ci.yml' #- local: '/src/l3_centralizedattackdetector/.gitlab-ci.yml' #- local: '/src/l3_attackmitigator/.gitlab-ci.yml' - local: '/src/slice/.gitlab-ci.yml' #- local: '/src/interdomain/.gitlab-ci.yml' - local: '/src/pathcomp/.gitlab-ci.yml' #- local: '/src/dlt/.gitlab-ci.yml' - local: '/src/load_generator/.gitlab-ci.yml' - local: '/src/bgpls_speaker/.gitlab-ci.yml' - local: '/src/kpi_manager/.gitlab-ci.yml' - local: '/src/kpi_value_api/.gitlab-ci.yml' #- local: '/src/kpi_value_writer/.gitlab-ci.yml' #- local: '/src/telemetry/.gitlab-ci.yml' - local: '/src/analytics/.gitlab-ci.yml' - local: '/src/qos_profile/.gitlab-ci.yml' - local: '/src/vnt_manager/.gitlab-ci.yml' - local: '/src/e2e_orchestrator/.gitlab-ci.yml' - local: '/src/ztp_server/.gitlab-ci.yml' - local: '/src/osm_client/.gitlab-ci.yml' - local: '/src/simap_connector/.gitlab-ci.yml' - local: '/src/pluggables/.gitlab-ci.yml' # #- local: '/manifests/.gitlab-ci.yml' # - local: '/src/monitoring/.gitlab-ci.yml' # - local: '/src/nbi/.gitlab-ci.yml' # - local: '/src/context/.gitlab-ci.yml' # - local: '/src/device/.gitlab-ci.yml' # - local: '/src/service/.gitlab-ci.yml' # - local: '/src/qkd_app/.gitlab-ci.yml' # - local: '/src/dbscanserving/.gitlab-ci.yml' # - local: '/src/opticalattackmitigator/.gitlab-ci.yml' # - local: '/src/opticalattackdetector/.gitlab-ci.yml' # - local: '/src/opticalattackmanager/.gitlab-ci.yml' # - local: '/src/opticalcontroller/.gitlab-ci.yml' # - local: '/src/ztp/.gitlab-ci.yml' # - local: '/src/policy/.gitlab-ci.yml' # - local: '/src/automation/.gitlab-ci.yml' # - local: '/src/forecaster/.gitlab-ci.yml' # #- local: '/src/webui/.gitlab-ci.yml' # #- local: '/src/l3_distributedattackdetector/.gitlab-ci.yml' # #- local: '/src/l3_centralizedattackdetector/.gitlab-ci.yml' # #- local: '/src/l3_attackmitigator/.gitlab-ci.yml' # - local: '/src/slice/.gitlab-ci.yml' # #- local: '/src/interdomain/.gitlab-ci.yml' # - local: '/src/pathcomp/.gitlab-ci.yml' # #- local: '/src/dlt/.gitlab-ci.yml' # - local: '/src/load_generator/.gitlab-ci.yml' # - local: '/src/bgpls_speaker/.gitlab-ci.yml' # - local: '/src/kpi_manager/.gitlab-ci.yml' # - local: '/src/kpi_value_api/.gitlab-ci.yml' # #- local: '/src/kpi_value_writer/.gitlab-ci.yml' # #- local: '/src/telemetry/.gitlab-ci.yml' # - local: '/src/analytics/.gitlab-ci.yml' # - local: '/src/qos_profile/.gitlab-ci.yml' # - local: '/src/vnt_manager/.gitlab-ci.yml' # - local: '/src/e2e_orchestrator/.gitlab-ci.yml' # - local: '/src/ztp_server/.gitlab-ci.yml' # - local: '/src/osm_client/.gitlab-ci.yml' # - local: '/src/simap_connector/.gitlab-ci.yml' # - local: '/src/pluggables/.gitlab-ci.yml' # This should be last one: end-to-end integration tests - local: '/src/tests/.gitlab-ci.yml'
hackfest/mock_osm/__main__.py +1 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ import cmd, logging from .MockOSM import MockOSM logging.basicConfig(level=logging.DEBUG) LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) Loading
src/device/service/driver_api/DriverFactory.py +15 −9 Original line number Diff line number Diff line Loading @@ -14,8 +14,7 @@ import logging from enum import Enum from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Type from ._Driver import _Driver from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Set, Tuple, Type from .Exceptions import ( AmbiguousFilterException, EmptyFilterFieldException, UnsatisfiedFilterException, UnsupportedDriverClassException, Loading @@ -23,12 +22,20 @@ from .Exceptions import ( ) from .FilterFields import FILTER_FIELD_ALLOWED_VALUES, FilterFieldEnum if TYPE_CHECKING: from ._Driver import _Driver LOGGER = logging.getLogger(__name__) SUPPORTED_FILTER_FIELDS = set(FILTER_FIELD_ALLOWED_VALUES.keys()) def check_is_class_valid(driver_class : Type['_Driver']) -> None: from ._Driver import _Driver if not issubclass(driver_class, _Driver): raise UnsupportedDriverClassException(str(driver_class)) def sanitize_filter_fields( filter_fields : Dict[FilterFieldEnum, Any], driver_name : Optional[str] = None ) -> Dict[FilterFieldEnum, Any]: Loading Loading @@ -67,14 +74,13 @@ def sanitize_filter_fields( class DriverFactory: def __init__( self, drivers : List[Tuple[Type[_Driver], List[Dict[FilterFieldEnum, Any]]]] self, drivers : List[Tuple[Type['_Driver'], List[Dict[FilterFieldEnum, Any]]]] ) -> None: self.__drivers : List[Tuple[Type[_Driver], Dict[FilterFieldEnum, Any]]] = list() self.__drivers : List[Tuple[Type['_Driver'], Dict[FilterFieldEnum, Any]]] = list() for driver_class,filter_field_sets in drivers: #if not issubclass(driver_class, _Driver): # raise UnsupportedDriverClassException(str(driver_class)) driver_name = driver_class #.__name__ check_is_class_valid(driver_class) driver_name = driver_class.__name__ for filter_fields in filter_field_sets: filter_fields = {k.value:v for k,v in filter_fields.items()} Loading Loading @@ -102,7 +108,7 @@ class DriverFactory: return True def get_driver_class(self, **selection_filter_fields) -> _Driver: def get_driver_class(self, **selection_filter_fields) -> '_Driver': sanitized_filter_fields = sanitize_filter_fields(selection_filter_fields) compatible_drivers : List[Tuple[Type[_Driver], Dict[FilterFieldEnum, Any]]] = [ Loading
src/device/service/driver_api/Exceptions.py +12 −10 Original line number Diff line number Diff line Loading @@ -13,22 +13,22 @@ # limitations under the License. class UnsatisfiedFilterException(Exception): def __init__(self, filter_fields): def __init__(self, filter_fields) -> None: msg = 'No Driver satisfies FilterFields({:s})' super().__init__(msg.format(str(filter_fields))) class AmbiguousFilterException(Exception): def __init__(self, filter_fields, compatible_drivers): def __init__(self, filter_fields, compatible_drivers) -> None: msg = 'Multiple Drivers satisfy FilterFields({:s}): {:s}' super().__init__(msg.format(str(filter_fields), str(compatible_drivers))) class UnsupportedDriverClassException(Exception): def __init__(self, driver_class_name): def __init__(self, driver_class_name) -> None: msg = 'Class({:s}) is not a subclass of _Driver' super().__init__(msg.format(str(driver_class_name))) class EmptyFilterFieldException(Exception): def __init__(self, filter_fields, driver_class_name=None): def __init__(self, filter_fields, driver_class_name=None) -> None: if driver_class_name: msg = 'Empty FilterField({:s}) specified by Driver({:s}) is not supported' msg = msg.format(str(filter_fields), str(driver_class_name)) Loading @@ -38,7 +38,7 @@ class EmptyFilterFieldException(Exception): super().__init__(msg) class UnsupportedFilterFieldException(Exception): def __init__(self, unsupported_filter_fields, driver_class_name=None): def __init__(self, unsupported_filter_fields, driver_class_name=None) -> None: if driver_class_name: msg = 'FilterFields({:s}) specified by Driver({:s}) are not supported' msg = msg.format(str(unsupported_filter_fields), str(driver_class_name)) Loading @@ -48,7 +48,9 @@ class UnsupportedFilterFieldException(Exception): super().__init__(msg) class UnsupportedFilterFieldValueException(Exception): def __init__(self, filter_field_name, filter_field_value, allowed_filter_field_values, driver_class_name=None): def __init__( self, filter_field_name, filter_field_value, allowed_filter_field_values, driver_class_name=None ) -> None: if driver_class_name: msg = 'FilterField({:s}={:s}) specified by Driver({:s}) is not supported. Allowed values are {:s}' msg = msg.format( Loading @@ -60,24 +62,24 @@ class UnsupportedFilterFieldValueException(Exception): super().__init__(msg) class DriverInstanceCacheTerminatedException(Exception): def __init__(self): def __init__(self) -> None: msg = 'DriverInstanceCache is terminated. No new instances can be processed.' super().__init__(msg) class UnsupportedResourceKeyException(Exception): def __init__(self, resource_key): def __init__(self, resource_key) -> None: msg = 'ResourceKey({:s}) not supported' msg = msg.format(str(resource_key)) super().__init__(msg) class ConfigFieldNotFoundException(Exception): def __init__(self, config_field_name): def __init__(self, config_field_name) -> None: msg = 'ConfigField({:s}) not specified in resource' msg = msg.format(str(config_field_name)) super().__init__(msg) class ConfigFieldsNotSupportedException(Exception): def __init__(self, config_fields): def __init__(self, config_fields) -> None: msg = 'ConfigFields({:s}) not supported in resource' msg = msg.format(str(config_fields)) super().__init__(msg)
src/device/service/drivers/gnmi_openconfig/handlers/InterfaceSwitchedVlan.py 0 → 100644 +104 −0 Original line number Diff line number Diff line # Copyright 2022-2025 ETSI 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, re from typing import Any, Dict, List, Tuple from ._Handler import _Handler from .Tools import get_str from .YangHandler import YangHandler LOGGER = logging.getLogger(__name__) RE_IF_SWITCHED_VLAN = re.compile(r'^/interface\[(?:name=)?([^\]]+)\]/ethernet/switched-vlan$') class InterfaceSwitchedVlanHandler(_Handler): def get_resource_key(self) -> str: return '/interface/ethernet/switched-vlan' def get_path(self) -> str: return '/openconfig-interfaces:interfaces/interface/ethernet/switched-vlan' def _get_interface_name(self, resource_key : str, resource_value : Dict) -> str: if 'name' in resource_value: return get_str(resource_value, 'name') if 'interface' in resource_value: return get_str(resource_value, 'interface') match = RE_IF_SWITCHED_VLAN.match(resource_key) if match is None: MSG = 'Interface name not found in resource_key={:s} resource_value={:s}' raise Exception(MSG.format(str(resource_key), str(resource_value))) return match.groups()[0] def _normalize_config(self, resource_value : Dict) -> Dict[str, Any]: config = resource_value.get('config') if isinstance(config, dict): return config interface_mode = resource_value.get('interface-mode', resource_value.get('interface_mode')) if interface_mode is None: raise Exception('interface-mode is required for switched-vlan config') interface_mode = str(interface_mode).upper() config = {'interface-mode': interface_mode} if interface_mode == 'ACCESS': access_vlan = resource_value.get('access-vlan', resource_value.get('access_vlan')) if access_vlan is None: raise Exception('access-vlan is required for ACCESS mode') config['access-vlan'] = int(access_vlan) elif interface_mode == 'TRUNK': native_vlan = resource_value.get('native-vlan', resource_value.get('native_vlan', 1)) config['native-vlan'] = int(native_vlan) trunk_vlans = resource_value.get('trunk-vlans', resource_value.get('trunk_vlans')) if trunk_vlans is None: trunk_vlan = resource_value.get('trunk-vlan', resource_value.get('trunk_vlan')) trunk_vlans = [trunk_vlan] if trunk_vlan is not None else [] if not isinstance(trunk_vlans, list): trunk_vlans = [trunk_vlans] config['trunk-vlans'] = [int(vlan) for vlan in trunk_vlans if vlan is not None] else: raise Exception('Unsupported interface-mode: {:s}'.format(str(interface_mode))) return config def compose( self, resource_key : str, resource_value : Dict, yang_handler : YangHandler, delete : bool = False ) -> Tuple[str, str]: if_name = self._get_interface_name(resource_key, resource_value) str_path = '/interfaces/interface[name={:s}]/ethernet/switched-vlan'.format(if_name) if delete: return str_path, json.dumps({}) config = self._normalize_config(resource_value) str_data = json.dumps({'config': config}) return str_path, str_data def parse( self, json_data : Dict, yang_handler : YangHandler ) -> List[Tuple[str, Dict[str, Any]]]: json_data_valid = yang_handler.parse_to_dict( '/openconfig-interfaces:interfaces', json_data, fmt='json', strict=False ) entries = [] for interface in json_data_valid.get('interfaces', {}).get('interface', []): interface_name = interface['name'] ethernet = interface.get('ethernet', {}) switched_vlan = ethernet.get('switched-vlan') if switched_vlan is None: continue entry_key = '/interface[{:s}]/ethernet/switched-vlan'.format(interface_name) entry_value = {} if 'config' in switched_vlan: entry_value['config'] = switched_vlan['config'] if 'state' in switched_vlan: entry_value['state'] = switched_vlan['state'] entries.append((entry_key, entry_value)) return entries