Resolve "Implement a new Firewall Agent controllable through RESTCONF/OpenConfig"

Closes #344 (closed)

Merge request reports

Loading
+1 −0
Changes for proto/context.proto: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -254,6 +254,7 @@ enum DeviceDriverEnum {
  DEVICEDRIVER_RYU = 18;
  DEVICEDRIVER_GNMI_NOKIA_SRLINUX = 19;
  DEVICEDRIVER_OPENROADM = 20;
  DEVICEDRIVER_RESTCONF_OPENCONFIG = 21;
}

enum DeviceOperationalStatusEnum {
+25 −0
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
+4 −0
Changes for src/common/tools/rest_conf/client/RestConfClient.py: 4 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -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(
+38 −13
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
@@ -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:
@@ -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(
@@ -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:
@@ -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:
@@ -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:
@@ -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__':
+9 −4
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
@@ -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(
@@ -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
        )

@@ -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
        )

@@ -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
        )

@@ -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