Add some basic usage documentation

This adds some basic usage documentation and a simple tutorial for
ovsdbapp.

Change-Id: I19583b439f0d8488e957265d89309abf08d307e7
This commit is contained in:
Terry Wilson 2023-07-18 15:07:55 -05:00
parent d542e5cee1
commit 6f7aed7098
3 changed files with 193 additions and 5 deletions

View File

@ -1,7 +1,20 @@
=====
Usage
=====
==================
Library User Guide
==================
To use ovsdbapp in a project::
This document describes OVSDBapp concepts and best practices to enable
writing effective OVSDBapp-based applications.
import ovsdbapp
Overview
--------
.. toctree::
:maxdepth: 2
overview
Tutorial
--------
.. toctree::
:maxdepth: 2
tutorial

View File

@ -0,0 +1,48 @@
========
Overview
========
OVSDBapp is a library to make it easier to write applications that interact
with an Open vSwitch database server. It allows the user to separate support
for a particular OVSDB schema and the backend method of communication with the
OVSDB server.
OVSDBapp Concepts
-----------------
API
The interface that an application will use for reading or modifying entries
in the OVS database. Whatever backend communication method is used, as long
as user code only accesses methods in this API, no user code should need to
be changed when swapping between backends.
Backend
The Backend handles the communication with Open vSwitch. Originally, there
were two OVSDBapp backends: 1) one that used the ovs-vsctl CLI utility to
interact with the OVS database and 2) one that maintains a persistent
connection to an OVSDB server using the python-ovs library. Currently, only
the python-ovs backend is being maintained.
Command
OVSDBapp uses the `Command Pattern`_ to isolate individual units of work
that will be run as part of an OVSDB transaction.
Event
OVSDB provides the ability to monitor database changes as they happen.
OVSDBapp backends each implement the :code:`RowEvent` and
:code:`RowEventHandler` to handle delivering these events to user code.
API Implementations:
The backend-specific implementation of an OVSDBapp API. Only this code
should need to be implemented to support a new backend. All other user
code should be backend-agnostic.
Schema
The OVSDB database schema for which the API is implemented. In current
ovsdbapp code, the schema and API are intrinsically linked in a
1:1 manner, but ultimately they are independent. User code could easily
define an API specific to their application that encompasses multiple
OVSDB schemas as long as the Backend supported it.
Transaction
An OVSDB transaction consisting of one or more Commands.
Virtual Environment
OVSDBapp supports running OVS and OVN services in a virtual environment.
This is primarily used for testing.
.. _Command Pattern: https://en.wikipedia.org/wiki/Command_pattern

View File

@ -0,0 +1,127 @@
========
Tutorial
========
Open vSwitch Environment Setup
------------------------------
This tutorial will use the Open vSwitch sandbox environment from the OVS
source tree. For the sake of simplicity, we will build OVS without SSL support.
You will need git, C development tools, automake, autoconf, and libtool. See
the `Installing Open vSwitch`_ instructions for build requirements
and more detailed build instructions.
.. code-block:: shell
git clone https://github.com/openvswitch/ovs
cd ovs
./boot.sh
./configure --disable-ssl --enable-shared
export OVS_SRCDIR=`pwd`
make -j $(nproc) sandbox
Backend Setup
-------------
While the original ovs-vsctl -based backend required no setup, other backends
may. For example, the python-ovs IDL backend maintains a constant connection
to ovsdb-server and requires an IDL class to be instantiated and passed to
an OVSDBapp IDL backend Connection object.
.. code-block:: python
import os
from ovs.db import idl as ovs_idl
from ovsdbapp.backend.ovs_idl import connection
from ovsdbapp.schema.open_vswitch import impl_idl
src_dir = os.getenv("OVS_SRCDIR")
run_dir = os.getenv("OVS_RUNDIR", "/var/run/openvswitch")
schema_file = os.path.join(src_dir, "vswitchd", "vswitch.ovsschema")
db_sock = os.path.join(run_dir, "db.sock")
remote = f"unix:{db_sock}"
schema_helper = ovs_idl.SchemaHelper(schema_file)
schema_helper.register_all()
idl = ovs_idl.Idl(remote, schema_helper)
conn = connection.Connection(idl=idl, timeout=60)
api = impl_idl.OvsdbIdl(conn)
Using the API
-------------
Each API definition varies based on the schemas it supports and what the
app requires. There is built-in support for many common OVS and OVN-related
schemas, but it is possible that the APIs defined for these are not optimized
for a given app's use cases. It may often make sense for apps to define APIs
separate from those that are in ovsdbapp.
With that said, any api that inherits from ovsdbapp.api.API will at least
have methods defined for the standard generic OVSDB DB operations found
described in the `ovs-vsctl manpage`_ under Database Commands.
* list
* find
* get
* set
* add
* remove
* clear
* create
* destroy
They are all prefixed with db\_ (e.g. list becomes db_list) and have an
interface similar to that used by ovs-vsctl, ovn-nbctl, ovn-sbctl, etc.
db_list() and db_find() return results as lists of dicts with each dict
representing a row, with keys as the column names. Later versions added
db_list_rows() and db_find_rows() to return lists of RowView objects.
API commands that interact with the OVSDB server typically return an instance
of a subclass of ovsdbapp.api.Command. These objects hold the state of a
request that will be sent to an OVSDB server as part of a transaction. They
can be thought of as the equivalent of queries in SQL.
For a Command to be sent to the OVSDB server, it must be attached to a
transaction, and committed. For single commands, this can be done with
execute():
.. code-block:: python
results = api.db_list("Open_vSwitch").execute(check_error=True)
This implicitly creates a transaction, adds the Command returned by db_list()
to that transaction, calls commit() on the transaction, and returns the result
that is stored on the Command object. It is the equivalent of:
.. code-block:: python
txn = api.create_transaction(check_error=True)
list_cmd = api.db_list("Open_vSwitch")
txn.add(list_cmd)
txn.commit()
results = list_cmd.result
That API also defines transaction(), a context manager, that makes
multi-command transactions easier.
.. code-block:: python
with api.transaction(check_error=True) as txn:
br_cmd = txn.add(api.db_create("Bridge", name="test-br"))
txn.add(api.db_add("Open_vSwitch", ".", "bridges", br_cmd))
There are some things to note with the above code. First, is that
Transaction.add() returns the Command object that is passed to it. In the case
of the db_create() command, the row it will create can be referenced in other
commands in the same transaction. Second, if a table is defined as having at
most one row, like the Open_vSwitch table, instead of passing its UUID, "."
can be passed. Lastly, note that we are creating a Bridge row and adding it to
the Open_vSwitch row's "bridges" field. The Bridge table is not set as a "root"
table in the Open_vSwitch schema. What this means is that if no row in a root
table references this Bridge, ovsdb-server will automatically clean up this
row. The Open_vSwitch table is a root table, so referencing the bridge in that
row prevents the bridge that was just created from being immediately removed.
.. _Installing Open vSwitch: https://docs.openvswitch.org/en/latest/intro/install/
.. _ovs-vsctl manpage: http://www.openvswitch.org/support/dist-docs/ovs-vsctl.8.html