Documentation - vmware-archive/ovsdb-client-library GitHub Wiki

Table of Content

Architecture

The OVSDB client library consists of two modules: OVSDB Client module and JSON-RPC module. OVSDB Client module provides the OvsdbClient object which is used by the user to communicate with an OVSDB server. It depends on JSON-RPC module to send JSON-RPC requests and receive JSON-PRC responses. JSON-RPC module is transparent to the user and is independent from the OVSDB logic. Netty is used for network operations.

A user can get an active OvsdbClient object through a passive connection or an active connection. The sequences of two connection types are shown in Passive Connection and Active Connection sections respectively. The user will use the OvsdbClient object to call OVSDB methods on the OVSDB server, as shown in Call Methods.

Passive Connectiond

(1) The user starts listening on port 6640 by OvsdPassiveConnectionListener.
(2) OvsdPassiveConnectionListener calls a Netty API to listen on port 6640.
(3) User gets notified when the listening starts successfully.
(4) An OVSDB server actively connects to port 6640.
(5) Netty establishes the connection to the OVSDB server.
(6) One of the Netty handlers creates an OvsdbClient object of this connection and notifies the user by the callback provided by the user.

Active Connection

(1) The user tries to connects to an OVSDB server on <ip>:<port> by OvsdActiveConnectionConnector.
(2) OvsdActiveConnectionConnector calls a Netty API to connect to <ip>:<port>.
(3) Netty initiates the connection to <ip>:<port>.
(4) The connection is established.
(5) One of the Netty handlers creates an OvsdbClient object of this connection and notifies the user by the CompletableFuture returned to user before.

Call Methods

(1) The user calls a method such as transact() through OvsdbClient.
(2) OvsdbClient calls the JSON-RPC API call() with method name "transact".
(3) JSON-RPC module converts the request to a JSON string and sends it through the transporter (also supported by Netty).
(4) The transporter converts the string to byte stream and sends it to the OVSDB server.
(5) After processing the request, the OVSDB server sends back the response in byte stream.
(6) The transporter (Netty) converts the byte stream into JSON response string and returns it to JSON-RPC module.
(7) JSON-RPC module converts the result to appropriate type and returns it to the OvsdbClient.
(8) The user gets the result.

Modules

JSON-RPC

JSON-RPC Protocol 1.0

From RFC 7047:

The OVSDB management protocol uses JSON for its wire format and is based on JSON-RPC version 1.0.

Thus the JSON-RPC module in the library implements JSON-RPC 1.0.

Request

The class that represents a JSON-RPC 1.0 request is: JsonRpcV1Request. It contains 3 members:

  • String method - A String containing the name of the method to be invoked.
  • ArrayNode params - An Array of objects to pass as arguments to the method. The type is ArrayNode instead of a List of Object because when a JSON-RPC server deserializes a JSON-RPC request from the received string, it doesn't know the type of the params. Thus, it can only deserializes the params into an ArrayNode.
  • String id - The request id. Though JSON-RPC 1.0 allows an ID to by any type, we define it as String for simplicity.

Note: A notification is represented by the same class but with id being null.

Response

The class that represents a JSON-RPC 1.0 response is: JsonRpcV1Response. It contains 3 members:

  • JsonNode result - The Object that was returned by the invoked method. This must be null in case there was an error invoking the method. The type is JsonNode instead of an Object because when a JSON-RPC Client deserializes a JSON-RPC response from the received string, it doesn't know the type of the result. Thus, it can only deserializes the result into a JsonNode.
  • String error - A String contains the error invoking the method. It must be null if there was no error.
  • String id - This must be the same id as the request it is responding to.

JSON-RPC Transporter

Both a JSON-RPC client and a JSON-RPC server depends on a JSON-RPC transporter. The JSON-RPC module doesn't provide an implementation of the transporter. Instead, the upper level application should implement one and pass it to JSON-RPC client or server. The interface of a JSON-RPC transporter is defined as follows:

public interface JsonRpcTransporter {
  void send(JsonNode data) throws JsonRpcTransportException;
  void close();
}

JsonRpcTransporter defines only two methods:

  • send() - Send a JsonNode to the remote peer. It throws a JsonRpcTransportException if fails to send the data.
  • close() - Close this transporter. The transporter cannot be reused after close() is called.

JSON-RPC Client

JSON-RPC client interface is defined as follows:

public interface JsonRpcV1Client {
  <T> CompletableFuture<T> call(String id, String method, Class<T> returnType, Object... params)
      throws JsonRpcException;
  void notify(String method, Object... params) throws JsonRpcException;
  void handleResponse(JsonNode responseNode) throws JsonRpcException;
  void shutdown();
}

JsonRpcV1Client defines four methods:

  • call() - Call a RPC method with given params. Request ID is provided by the caller. The return type of this call is also provided so that it knows how to convert the result from a JSON object. This call is asynchronous and returns a CompletableFurture of the return value. It throws a JsonRpcException if fails to call the method.
  • notify() - Similar to call() but without a request id and return value.
  • handleResponse() - This is called by the transporter after receiving a response from the peer. A JsonRpcException is thrown if it fails to handle the response.
  • shutdown() - Shut down the client. After it is called, the client cannot be reused.

Implementation of this interface can be found here. Its constructor is defined as follows:

public JsonRpcV1ClientImpl(
    JsonRpcTransporter transporter, ScheduledExecutorService scheduler,
    long maxTimeout, TimeUnit maxTimeoutUnit) {
    //...
  }

The JsonRpcV1ClientImpl takes a JsonRpcTransporter for sending the request to peer; a ScheduledExecutorService for clearing the call context of a request when it times out or when its response never comes( This can be removed after the library is upgraded to Java 9 so that the asynchronous timeout of CompletableFuture is supported); and a timeout value of the request and its corresponding time unit.

JSON-RPC Server

JSON-RPC server interface is defined as follows:

public interface JsonRpcV1Server {
  void handleRequest(JsonNode requestNode) throws JsonRpcException;
  void shutdown();
}

JsonRpcV1Server defines four methods:

  • handleRequest() - This is called by the transporter after receiving a request from the peer. A JsonRpcException is thrown if it fails to handle the request.
  • shutdown() - Shut down the server. After it is called, the server cannot be reused.

Implementation of this interface can be found here. Its constructor is defined as follows:

public JsonRpcV1ServerImpl(JsonRpcTransporter transporter, Object requestHandler) {
 //...
}

The JsonRpcV1ServerImpl takes a JsonRpcTransporter for sending response to the peer and a request handler which contains methods annotated with JsonRpcServiceMethod that are used to handle the JSON-RPC requests. JsonRpcServiceMethod has a value that defines the JSON-RPC method name that this Java method can handle. By default, if the value is not set or empty, it is the same as the Java method name.

Class Diagram

OVSDB Client

OVSDB Management Protocol

All the Java representations of notations defined in RFC 7047 are in package com.vmware.ovsdb.protocol.

Netty Channel Handlers

Netty is an asynchronous event-driven network application framework. It is used in this library for network operations so that we do not need to write raw Java NIO code, which is error-prone. You can find the Netty user guide here.

In both passive and active connection mode, we need to initialize the Netty channel to the peer by setting up the handler pipeline. The handlers we use are: Idle stat handler, SSL handler (optional), logging handler, JSON decoder, String encoder, heartbeat handler, OVSDB client handler and exception handler.

Idle State Handler

IdleStateHandler triggers an IdleStateEvent when a channel has not performed read, write, or both operation for a while. In this library, only read idle detection is enabled. Idle state handler is a duplex handler.

SSL Handler

SslHandler is responsible for SSL handshake, encryption of outbound data and description of incoming data. This handler is optional and is only added if the user connects/listens with SSL enabled. SSL handler is a duplex handler.

Logging Handler

LoggingHandler is used to log inbound and outbound data. It can be useful for debugging. By default, the log level of this handler is TRACE. Logging handler is a duplex handler.

JSON Decoder

JsonNodeDecoder decodes a ByteBuf into a JsonNode. It is an inbound handler.

String Encoder

StringEncoder encodes a String into a ByteBuf. It is an outbound handler.

Connection Handler

OvsdbConnectionHandler is responsible for notify the connection of an OVSDB server and handling the channel READ idle event. It has the following jobs:

  • After a channel becomes active (connected) and after the SSL handshake is done if any, the handler creates an OvsdbClient from the channel and notifies the user with this connection.
  • After a channel becomes inactive (disconnected), the handler notifies the user with this disconnection.
  • When there is a channel read idle event triggered, the handler sends an "echo" request (heartbeat) to detect the liveness of peer. If after certain number of echo requests the peer still does not response, then the handler will close the channel.

OvsdbConnectionHandler is an inbound handler.

JSON-RPC Handler

JsonRpcHandler is added to the channel pipeline only when an OvsdbClient is created. It depends on a JsonRpcV1Client to handle JSON-RPC response and a JsonRpcV1Server to handle JSON-RPC request. JsonRpcHandler is an inbound handler.

Exception Handler

ExceptionHandler is the last handler in the pipeline and is used to handler any exception that is not handled by previous handlers. It simply close the channel in case of an exception.

Arrangement Diagram

The arrangement of the handlers is shown as follows:

Netty Channel Initializer

Both passive and active connection uses OvsdbChannelInitializer to initialize the channel by setting up aforementioned handlers. Based on whether this is a passive or active connection channel, the initializer will configure the SSL handler in server mode or client mode. It also decides whether the OvsdbConnectionHandler should call the connection callback or complete the CompletableFuture after the channel becomes active.

OVSDB Client Implementation

OvsdbClientImpl is the implementation of OvsdbClient interface. It depends on a ScheduledExecutorService and a Netty Channel:

public OvsdbClientImpl(ScheduledExecutorService executorService, Channel channel) {
  //...
}

A very important thing to notice about an OVSDB client is, it is both a JSON-RPC client and a JSON-RPC server. Because it sends RPC requests such as transact and monitor to an OVSDB server as well as handle requests such as echo and notifications such as update and locked from the OVSDB server. Thus it creates a JsonRpcV1Client and a JsonRpcV1Server. It also adds a JSON-RPC handler with the created JSON-RPC client and server to the Netty channel handler pipeline.

Class Diagram

⚠️ **GitHub.com Fallback** ⚠️