Documentation - vmware-archive/ovsdb-client-library GitHub Wiki
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.
(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.
(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.
(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.
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.
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 isArrayNode
instead of aList
ofObject
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 anArrayNode
. -
String id
- The request id. Though JSON-RPC 1.0 allows an ID to by any type, we define it asString
for simplicity.
Note: A notification is represented by the same class but with id
being null
.
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 isJsonNode
instead of anObject
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 aJsonNode
. -
String error
- AString
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.
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 aJsonNode
to the remote peer. It throws aJsonRpcTransportException
if fails to send the data. -
close()
- Close this transporter. The transporter cannot be reused afterclose()
is called.
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 aCompletableFurture
of the return value. It throws aJsonRpcException
if fails to call the method. -
notify()
- Similar tocall()
but without a request id and return value. -
handleResponse()
- This is called by the transporter after receiving a response from the peer. AJsonRpcException
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 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. AJsonRpcException
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.
All the Java representations of notations defined in RFC 7047 are in package com.vmware.ovsdb.protocol.
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.
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.
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.
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.
JsonNodeDecoder
decodes a ByteBuf
into a JsonNode
. It is an inbound handler.
StringEncoder
encodes a String
into a ByteBuf
. It is an outbound 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.
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.
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.
The arrangement of the handlers is shown as follows:
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.
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.