Loading proto/context.proto +1 −0 Viewed Changes for proto/context.proto: 1 added line, 0 removed lines. Original line number Diff line number Diff line Loading @@ -254,6 +254,7 @@ enum DeviceDriverEnum { DEVICEDRIVER_RYU = 18; DEVICEDRIVER_GNMI_NOKIA_SRLINUX = 19; DEVICEDRIVER_OPENROADM = 20; DEVICEDRIVER_RESTCONF_OPENCONFIG = 21; } enum DeviceOperationalStatusEnum { Loading scripts/run_tests_locally-device-restconf-openconfig.sh 0 → 100755 +25 −0 Viewed Changes for scripts/run_tests_locally-device-restconf-openconfig.sh: 25 added lines, 0 removed lines. Original line number Diff line number Diff line #!/bin/bash # 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. PROJECTDIR=`pwd` cd $PROJECTDIR/src RCFILE=$PROJECTDIR/coverage/.coveragerc # Run unitary tests and analyze coverage of code at same time # helpful pytest flags: --log-level=INFO -o log_cli=true --verbose --maxfail=1 --durations=0 coverage run --rcfile=$RCFILE --append -m pytest --log-level=DEBUG --verbose -o log_cli=true \ device/tests/restconf_openconfig/test_unitary_restconf_openconfig.py src/common/tools/rest_conf/client/RestConfClient.py +4 −0 Viewed Changes for src/common/tools/rest_conf/client/RestConfClient.py: 4 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -61,6 +61,10 @@ class RestConfClient(RestApiClient): self._base_url = str(href).replace('//', '/') if self._restconf_version is not None: self._base_url += '/{:s}'.format(self._restconf_version) if self._base_url.endswith('/data/'): self._base_url = self._base_url.split('/data/')[0] elif self._base_url.endswith('/data'): self._base_url = self._base_url.split('/data')[0] def get( Loading src/common/tools/rest_conf/server/restconf_server/Callbacks.py +38 −13 Viewed Changes for src/common/tools/rest_conf/server/restconf_server/Callbacks.py: 38 added lines, 13 removed lines. Original line number Diff line number Diff line Loading @@ -38,7 +38,22 @@ class _Callback: ''' return self._path_pattern.fullmatch(path) def execute_data( def execute_data_pre_get( self, match : re.Match, path : str, old_data : Optional[Dict] ) -> bool: ''' Execute the callback action for a matched data path. This method should be implemented for each specific callback. @param match: `re.Match` object returned by `match()`. @param path: Original request path that was matched. @param old_data: Resource representation before retrieval, if applicable, otherwise `None` @returns boolean indicating whether additional callbacks should be executed, defaults to False ''' MSG = 'match={:s}, path={:s}, old_data={:s}' msg = MSG.format(match.groupdict(), path, old_data) raise NotImplementedError(msg) def execute_data_update( self, match : re.Match, path : str, old_data : Optional[Dict], new_data : Optional[Dict] ) -> bool: Loading Loading @@ -78,14 +93,24 @@ class CallbackDispatcher: def register(self, callback : _Callback) -> None: self._callbacks.append(callback) def dispatch_data( def dispatch_data_pre_get( self, path : str, old_data : Optional[Dict] = None ) -> None: LOGGER.warning('[dispatch_data_pre_get] Checking Callbacks for path={:s}'.format(str(path))) for callback in self._callbacks: match = callback.match(path) if match is None: continue keep_running_callbacks = callback.execute_data_pre_get(match, path, old_data) if not keep_running_callbacks: break def dispatch_data_update( self, path : str, old_data : Optional[Dict] = None, new_data : Optional[Dict] = None ) -> None: LOGGER.warning('[dispatch_data] Checking Callbacks for path={:s}'.format(str(path))) LOGGER.warning('[dispatch_data_update] Checking Callbacks for path={:s}'.format(str(path))) for callback in self._callbacks: match = callback.match(path) if match is None: continue keep_running_callbacks = callback.execute_data(match, path, old_data, new_data) keep_running_callbacks = callback.execute_data_update(match, path, old_data, new_data) if not keep_running_callbacks: break def dispatch_operation( Loading Loading @@ -113,7 +138,7 @@ class CallbackOnNetwork(_Callback): pattern += r'/ietf-network:networks/network=(?P<network_id>[^/]+)' super().__init__(pattern) def execute_data( def execute_data_update( self, match : re.Match, path : str, old_data : Optional[Dict], new_data : Optional[Dict] ) -> bool: Loading @@ -127,7 +152,7 @@ class CallbackOnNode(_Callback): pattern += r'/node=(?P<node_id>[^/]+)' super().__init__(pattern) def execute_data( def execute_data_update( self, match : re.Match, path : str, old_data : Optional[Dict], new_data : Optional[Dict] ) -> bool: Loading @@ -141,7 +166,7 @@ class CallbackOnLink(_Callback): pattern += r'/ietf-network-topology:link=(?P<link_id>[^/]+)' super().__init__(pattern) def execute_data( def execute_data_update( self, match : re.Match, path : str, old_data : Optional[Dict], new_data : Optional[Dict] ) -> bool: Loading @@ -167,12 +192,12 @@ def main() -> None: callbacks.register(CallbackOnLink()) callbacks.register(CallbackShutdown()) callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin') callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin/node=P-PE2') callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin/ietf-network-topology:link=L6') callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin/') callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin/node=P-PE1/') callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin/ietf-network-topology:link=L4/') callbacks.dispatch_data_update('/restconf/data/ietf-network:networks/network=admin') callbacks.dispatch_data_update('/restconf/data/ietf-network:networks/network=admin/node=P-PE2') callbacks.dispatch_data_update('/restconf/data/ietf-network:networks/network=admin/ietf-network-topology:link=L6') callbacks.dispatch_data_update('/restconf/data/ietf-network:networks/network=admin/') callbacks.dispatch_data_update('/restconf/data/ietf-network:networks/network=admin/node=P-PE1/') callbacks.dispatch_data_update('/restconf/data/ietf-network:networks/network=admin/ietf-network-topology:link=L4/') callbacks.dispatch_operation('/restconf/operations/shutdown/') if __name__ == '__main__': Loading src/common/tools/rest_conf/server/restconf_server/DispatchData.py +9 −4 Viewed Changes for src/common/tools/rest_conf/server/restconf_server/DispatchData.py: 9 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -31,6 +31,11 @@ class RestConfDispatchData(Resource): self._callback_dispatcher = callback_dispatcher def get(self, subpath : str = '/') -> Response: data = self._yang_handler.get(subpath) self._callback_dispatcher.dispatch_data_pre_get( '/restconf/data/' + subpath, old_data=data ) data = self._yang_handler.get(subpath) if data is None: abort( Loading Loading @@ -70,7 +75,7 @@ class RestConfDispatchData(Resource): LOGGER.info('[POST] {:s} {:s} => {:s}'.format(subpath, str(payload), str(json_data))) self._callback_dispatcher.dispatch_data( self._callback_dispatcher.dispatch_data_update( '/restconf/data/' + subpath, old_data=None, new_data=json_data ) Loading Loading @@ -102,7 +107,7 @@ class RestConfDispatchData(Resource): diff_data = deepdiff.DeepDiff(old_data, new_data) updated = len(diff_data) > 0 self._callback_dispatcher.dispatch_data( self._callback_dispatcher.dispatch_data_update( '/restconf/data/' + subpath, old_data=old_data, new_data=new_data ) Loading Loading @@ -140,7 +145,7 @@ class RestConfDispatchData(Resource): #diff_data = deepdiff.DeepDiff(old_data, new_data) #updated = len(diff_data) > 0 self._callback_dispatcher.dispatch_data( self._callback_dispatcher.dispatch_data_update( '/restconf/data/' + subpath, old_data=old_data, new_data=new_data ) Loading Loading @@ -170,7 +175,7 @@ class RestConfDispatchData(Resource): description='Path({:s}) not found'.format(str(subpath)) ) self._callback_dispatcher.dispatch_data( self._callback_dispatcher.dispatch_data_update( '/restconf/data/' + subpath, old_data=old_data, new_data=None ) Loading Loading
proto/context.proto +1 −0 Viewed Changes for proto/context.proto: 1 added line, 0 removed lines. Original line number Diff line number Diff line Loading @@ -254,6 +254,7 @@ enum DeviceDriverEnum { DEVICEDRIVER_RYU = 18; DEVICEDRIVER_GNMI_NOKIA_SRLINUX = 19; DEVICEDRIVER_OPENROADM = 20; DEVICEDRIVER_RESTCONF_OPENCONFIG = 21; } enum DeviceOperationalStatusEnum { Loading
scripts/run_tests_locally-device-restconf-openconfig.sh 0 → 100755 +25 −0 Viewed Changes for scripts/run_tests_locally-device-restconf-openconfig.sh: 25 added lines, 0 removed lines. Original line number Diff line number Diff line #!/bin/bash # 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. PROJECTDIR=`pwd` cd $PROJECTDIR/src RCFILE=$PROJECTDIR/coverage/.coveragerc # Run unitary tests and analyze coverage of code at same time # helpful pytest flags: --log-level=INFO -o log_cli=true --verbose --maxfail=1 --durations=0 coverage run --rcfile=$RCFILE --append -m pytest --log-level=DEBUG --verbose -o log_cli=true \ device/tests/restconf_openconfig/test_unitary_restconf_openconfig.py
src/common/tools/rest_conf/client/RestConfClient.py +4 −0 Viewed Changes for src/common/tools/rest_conf/client/RestConfClient.py: 4 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -61,6 +61,10 @@ class RestConfClient(RestApiClient): self._base_url = str(href).replace('//', '/') if self._restconf_version is not None: self._base_url += '/{:s}'.format(self._restconf_version) if self._base_url.endswith('/data/'): self._base_url = self._base_url.split('/data/')[0] elif self._base_url.endswith('/data'): self._base_url = self._base_url.split('/data')[0] def get( Loading
src/common/tools/rest_conf/server/restconf_server/Callbacks.py +38 −13 Viewed Changes for src/common/tools/rest_conf/server/restconf_server/Callbacks.py: 38 added lines, 13 removed lines. Original line number Diff line number Diff line Loading @@ -38,7 +38,22 @@ class _Callback: ''' return self._path_pattern.fullmatch(path) def execute_data( def execute_data_pre_get( self, match : re.Match, path : str, old_data : Optional[Dict] ) -> bool: ''' Execute the callback action for a matched data path. This method should be implemented for each specific callback. @param match: `re.Match` object returned by `match()`. @param path: Original request path that was matched. @param old_data: Resource representation before retrieval, if applicable, otherwise `None` @returns boolean indicating whether additional callbacks should be executed, defaults to False ''' MSG = 'match={:s}, path={:s}, old_data={:s}' msg = MSG.format(match.groupdict(), path, old_data) raise NotImplementedError(msg) def execute_data_update( self, match : re.Match, path : str, old_data : Optional[Dict], new_data : Optional[Dict] ) -> bool: Loading Loading @@ -78,14 +93,24 @@ class CallbackDispatcher: def register(self, callback : _Callback) -> None: self._callbacks.append(callback) def dispatch_data( def dispatch_data_pre_get( self, path : str, old_data : Optional[Dict] = None ) -> None: LOGGER.warning('[dispatch_data_pre_get] Checking Callbacks for path={:s}'.format(str(path))) for callback in self._callbacks: match = callback.match(path) if match is None: continue keep_running_callbacks = callback.execute_data_pre_get(match, path, old_data) if not keep_running_callbacks: break def dispatch_data_update( self, path : str, old_data : Optional[Dict] = None, new_data : Optional[Dict] = None ) -> None: LOGGER.warning('[dispatch_data] Checking Callbacks for path={:s}'.format(str(path))) LOGGER.warning('[dispatch_data_update] Checking Callbacks for path={:s}'.format(str(path))) for callback in self._callbacks: match = callback.match(path) if match is None: continue keep_running_callbacks = callback.execute_data(match, path, old_data, new_data) keep_running_callbacks = callback.execute_data_update(match, path, old_data, new_data) if not keep_running_callbacks: break def dispatch_operation( Loading Loading @@ -113,7 +138,7 @@ class CallbackOnNetwork(_Callback): pattern += r'/ietf-network:networks/network=(?P<network_id>[^/]+)' super().__init__(pattern) def execute_data( def execute_data_update( self, match : re.Match, path : str, old_data : Optional[Dict], new_data : Optional[Dict] ) -> bool: Loading @@ -127,7 +152,7 @@ class CallbackOnNode(_Callback): pattern += r'/node=(?P<node_id>[^/]+)' super().__init__(pattern) def execute_data( def execute_data_update( self, match : re.Match, path : str, old_data : Optional[Dict], new_data : Optional[Dict] ) -> bool: Loading @@ -141,7 +166,7 @@ class CallbackOnLink(_Callback): pattern += r'/ietf-network-topology:link=(?P<link_id>[^/]+)' super().__init__(pattern) def execute_data( def execute_data_update( self, match : re.Match, path : str, old_data : Optional[Dict], new_data : Optional[Dict] ) -> bool: Loading @@ -167,12 +192,12 @@ def main() -> None: callbacks.register(CallbackOnLink()) callbacks.register(CallbackShutdown()) callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin') callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin/node=P-PE2') callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin/ietf-network-topology:link=L6') callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin/') callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin/node=P-PE1/') callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin/ietf-network-topology:link=L4/') callbacks.dispatch_data_update('/restconf/data/ietf-network:networks/network=admin') callbacks.dispatch_data_update('/restconf/data/ietf-network:networks/network=admin/node=P-PE2') callbacks.dispatch_data_update('/restconf/data/ietf-network:networks/network=admin/ietf-network-topology:link=L6') callbacks.dispatch_data_update('/restconf/data/ietf-network:networks/network=admin/') callbacks.dispatch_data_update('/restconf/data/ietf-network:networks/network=admin/node=P-PE1/') callbacks.dispatch_data_update('/restconf/data/ietf-network:networks/network=admin/ietf-network-topology:link=L4/') callbacks.dispatch_operation('/restconf/operations/shutdown/') if __name__ == '__main__': Loading
src/common/tools/rest_conf/server/restconf_server/DispatchData.py +9 −4 Viewed Changes for src/common/tools/rest_conf/server/restconf_server/DispatchData.py: 9 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -31,6 +31,11 @@ class RestConfDispatchData(Resource): self._callback_dispatcher = callback_dispatcher def get(self, subpath : str = '/') -> Response: data = self._yang_handler.get(subpath) self._callback_dispatcher.dispatch_data_pre_get( '/restconf/data/' + subpath, old_data=data ) data = self._yang_handler.get(subpath) if data is None: abort( Loading Loading @@ -70,7 +75,7 @@ class RestConfDispatchData(Resource): LOGGER.info('[POST] {:s} {:s} => {:s}'.format(subpath, str(payload), str(json_data))) self._callback_dispatcher.dispatch_data( self._callback_dispatcher.dispatch_data_update( '/restconf/data/' + subpath, old_data=None, new_data=json_data ) Loading Loading @@ -102,7 +107,7 @@ class RestConfDispatchData(Resource): diff_data = deepdiff.DeepDiff(old_data, new_data) updated = len(diff_data) > 0 self._callback_dispatcher.dispatch_data( self._callback_dispatcher.dispatch_data_update( '/restconf/data/' + subpath, old_data=old_data, new_data=new_data ) Loading Loading @@ -140,7 +145,7 @@ class RestConfDispatchData(Resource): #diff_data = deepdiff.DeepDiff(old_data, new_data) #updated = len(diff_data) > 0 self._callback_dispatcher.dispatch_data( self._callback_dispatcher.dispatch_data_update( '/restconf/data/' + subpath, old_data=old_data, new_data=new_data ) Loading Loading @@ -170,7 +175,7 @@ class RestConfDispatchData(Resource): description='Path({:s}) not found'.format(str(subpath)) ) self._callback_dispatcher.dispatch_data( self._callback_dispatcher.dispatch_data_update( '/restconf/data/' + subpath, old_data=old_data, new_data=None ) Loading