Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ovsp4rt] Implement Client class #732

Merged
merged 5 commits into from
Jan 8, 2025
Merged

[ovsp4rt] Implement Client class #732

merged 5 commits into from
Jan 8, 2025

Conversation

ffoulkes
Copy link
Contributor

@ffoulkes ffoulkes commented Jan 8, 2025

This is a project to improve the testability of ovsp4rt. See issue #701 for details.

  • Updated cmake listfile to use newovsp4rt.cc instead of ovsp4rt.cc when the BUILD_CLIENT option is enabled.

  • Modified newovsp4rt.cc to use a Client object instead of an OvsP4rtSession object to communicate with the P4Runtime server.

  • Made each public C API function a wrapper around a C++ function that accepts a Client object as a parameter. This allows a test to replace the object with a test double (e.g. a mock).

  • Moved the C API functions to another file (ovsp4rt_standard_api.cc), separating the user interface from the implementation.

  • Created a second implementation of the C API (ovsp4rt_journal_api.cc) that enables journaling.

  • Added an ovsp4rt_build_and_test job to the pipeline, to verify that ovsp4rt builds when BUILD_CLIENT and BUILD_JOURNAL are enabled.

Note

This PR adds a new file. To see the deltas between ovsp4rt.cc and newovsp4rt.cc, see 5ca68cb.

This PR supersedes PR #727.

Signed-off-by: Derek Foster <justffoulkes@gmail.com>
- Updated listfile to use newovsp4rt.cc instead of ovsp4rt.cc
  when the BUILD_CLIENT option is enabled.

- Modified newovsp4rt.cc to use a `Client` object instead of an
  `OvsP4rtSession` object to communicate with the P4Runtime server.

- Made each public C API function a wrapper around a C++ function
  that accepts a Client object as a parameter. This allows a unit
  test to replace the object with a test double (e.g. a mock).

- Moved the C API functions to another file, separating the user
  interface from the implementation.

Signed-off-by: Derek Foster <justffoulkes@gmail.com>
Signed-off-by: Derek Foster <justffoulkes@gmail.com>
Signed-off-by: Derek Foster <justffoulkes@gmail.com>
Signed-off-by: Derek Foster <justffoulkes@gmail.com>
@ffoulkes ffoulkes added enhancement New feature or request cmake Affects CMake build system github_actions Pull requests that update GitHub Actions code labels Jan 8, 2025
@@ -26,7 +26,7 @@ endif()
#-----------------------------------------------------------------------
add_library(ovs_sidecar_o OBJECT
${OVSP4RT_INCLUDE_DIR}/ovsp4rt/ovs-p4rt.h
ovsp4rt.cc
$<IF:$<BOOL:${BUILD_CLIENT}>,newovsp4rt.cc,ovsp4rt.cc>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a cmake generator expression. It selects newovsp4rt.cc if BUILD_CLIENT evaluates to TRUE, and ovsp4rt.cc if it evaluates to FALSE.

@@ -26,7 +26,7 @@ endif()
#-----------------------------------------------------------------------
add_library(ovs_sidecar_o OBJECT
${OVSP4RT_INCLUDE_DIR}/ovsp4rt/ovs-p4rt.h
ovsp4rt.cc
$<IF:$<BOOL:BUILD_CLIENT>,newovsp4rt.cc,ovsp4rt.cc>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This generator expression has a bug that's fixed in a subsequent commit.

@@ -13,7 +13,7 @@
namespace ovsp4rt {

extern void ConfigFdbEntry(ClientInterface& client,
const struct mac_learning_info& learn_info,
struct mac_learning_info learn_info,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

learn_info is passed by value because the ES2K version makes local modifications to it. This fact is mentioned in an implementation comment.

status_or_read_response =
GetTxAccVsiTableEntry(session.get(), learn_info.src_port, p4info);
if (!status_or_read_response.ok()) {
//
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proposed refactoring is implemented in #729. It will need to be revised for newovsp4rt.cc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit does two things.

  1. It replaces ovsp4rt::OvsP4rtSession* session with ClientInterface client&.
  2. It converts each of the C APIs into an internal C++ function (in this file) and a C wrapper function (in another file).

Replacing the explicit OvsP4rtSession object with an abstract ClientInterface object decouples the core logic from the P4Runtime server. We can now replace the server with a test double, such as a mock, giving us the ability to unit-test the code.

It also hides the details of the server interface, replacing a lot of duplicated code with much simpler method calls.

Splitting the interface into a C wrapper and a C++ implementation creates a seam. "A seam is a place where you can alter behavior in your program without editing in that place." (Michael Feathers, Working Effectively with Legacy Code)

Taken together, these two things give us testability.

if (!insert_entry) {
auto status_or_read_response =
GetL2ToTunnelV4TableEntry(session.get(), learn_info, p4info);
GetL2ToTunnelV4TableEntry(client, learn_info, p4info);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #728* defines predicate functions that allow us to simplify this code to:

if (HaveL2ToTunnelV4TableEntry(client, learn_info, p4info)) {
  learn_info.is_tunnel = true;
}

See commit 175a9f4 for details.

* This PR will need to be revised for newovsp4rt.cc.

// Unwrap the session from the StatusOr object.
std::unique_ptr<ovsp4rt::OvsP4rtSession> session =
std::move(status_or_session).value();
status = client.connect(grpc_addr);
Copy link
Contributor Author

@ffoulkes ffoulkes Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest reviewing the old server interface code against:

::p4::v1::ReadResponse read_response =
std::move(status_or_read_response).value();
//
// GetVsiSrcPort(ClientInterface& client, const P4Info& p4info,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See previous comment.

@@ -1,4 +1,4 @@
// Copyright 2022-2024 Intel Corporation
// Copyright 2022-2025 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

// Journaling implementation of the ovsp4rt C API.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an early draft of the API.

Copy link
Collaborator

@5abeel 5abeel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ffoulkes ffoulkes merged commit 4a92a84 into main Jan 8, 2025
10 checks passed
@ffoulkes ffoulkes deleted the newovsp4rt.cc branch January 8, 2025 22:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cmake Affects CMake build system enhancement New feature or request github_actions Pull requests that update GitHub Actions code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants