doc: Document for ryu.lib.ovs

Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
IWASE Yusuke 2018-02-05 09:46:45 +09:00 committed by FUJITA Tomonori
parent bd384997ac
commit 09d2811569
4 changed files with 264 additions and 4 deletions

View File

@ -15,3 +15,4 @@ Ryu provides some useful library for your network applications.
library_bgp_speaker_ref.rst library_bgp_speaker_ref.rst
library_mrt.rst library_mrt.rst
library_ovsdb_manager.rst library_ovsdb_manager.rst
library_ovsdb.rst

View File

@ -0,0 +1,76 @@
*************
OVSDB library
*************
Path: ``ryu.lib.ovs``
Similar to the :doc:`library_ovsdb_manager`, this library enables your
application to speak the OVSDB protocol (RFC7047_), but differ from the
:doc:`library_ovsdb_manager`, this library will initiate connections from
controller side as ovs-vsctl_ command does.
Please make sure that your devices are listening on either the Unix domain
socket or TCP/SSL port before calling the APIs of this library.
.. code-block:: bash
# Show current configuration
$ ovs-vsctl get-manager
# Set TCP listen address
$ ovs-vsctl set-manager "ptcp:6640"
See manpage of ovs-vsctl_ command for more details.
.. _RFC7047: https://tools.ietf.org/html/rfc7047
.. _ovs-vsctl: http://openvswitch.org/support/dist-docs/ovs-vsctl.8.txt
Basic Usage
===========
1. Instantiate :py:mod:`ryu.lib.ovs.vsctl.VSCtl`.
2. Construct commands with :py:mod:`ryu.lib.ovs.vsctl.VSCtlCommand`.
The syntax is almost the same as ovs-vsctl_ command.
3. Execute commands via :py:mod:`ryu.lib.ovs.vsctl.VSCtl.run_command`.
Example
-------
.. code-block:: python
from ryu.lib.ovs import vsctl
OVSDB_ADDR = 'tcp:127.0.0.1:6640'
ovs_vsctl = vsctl.VSCtl(OVSDB_ADDR)
# Equivalent to
# $ ovs-vsctl show
command = vsctl.VSCtlCommand('show')
ovs_vsctl.run_command([command])
print(command)
# >>> VSCtlCommand(args=[],command='show',options=[],result='830d781f-c3c8-4b4f-837e-106e1b33d058\n ovs_version: "2.8.90"\n')
# Equivalent to
# $ ovs-vsctl list Port s1-eth1
command = vsctl.VSCtlCommand('list', ('Port', 's1-eth1'))
ovs_vsctl.run_command([command])
print(command)
# >>> VSCtlCommand(args=('Port', 's1-eth1'),command='list',options=[],result=[<ovs.db.idl.Row object at 0x7f525fb682e8>])
print(command.result[0].name)
# >>> s1-eth1
API Reference
=============
ryu.lib.ovs.vsctl
-----------------
.. automodule:: ryu.lib.ovs.vsctl
:members:
ryu.lib.ovs.bridge
------------------
.. automodule:: ryu.lib.ovs.bridge
:members:

View File

@ -15,7 +15,7 @@
# limitations under the License. # limitations under the License.
""" """
slimmed down version of OVSBridge in quantum agent Wrapper utility library of :py:mod:`ryu.lib.ovs.vsctl`
""" """
import functools import functools
@ -92,6 +92,26 @@ class TunnelPort(object):
class OVSBridge(object): class OVSBridge(object):
"""
Class to provide wrapper utilities of :py:mod:`ryu.lib.ovs.vsctl.VSCtl`
``CONF`` is a instance of ``oslo_config.cfg.ConfigOpts``.
Mostly ``self.CONF`` is sufficient to instantiate this class from your Ryu
application.
``datapath_id`` specifies Datapath ID of the target OVS instance.
``ovsdb_addr`` specifies the address of the OVS instance.
Automatically validated when you call ``init()`` method.
Refer to :py:mod:`ryu.lib.ovs.vsctl.valid_ovsdb_addr` for the format of
this address.
if ``timeout`` is omitted, ``CONF.ovsdb_timeout`` will be used as the
default value.
Usage of ``timeout`` and ``exception`` is the same with ``timeout_sec``
and ``exception`` of :py:mod:`ryu.lib.ovs.vsctl.VSCtl.run_command`.
"""
def __init__(self, CONF, datapath_id, ovsdb_addr, timeout=None, def __init__(self, CONF, datapath_id, ovsdb_addr, timeout=None,
exception=None): exception=None):
@ -105,9 +125,25 @@ class OVSBridge(object):
self.br_name = None self.br_name = None
def run_command(self, commands): def run_command(self, commands):
"""
Executes the given commands and sends OVSDB messages.
``commands`` must be a list of
:py:mod:`ryu.lib.ovs.vsctl.VSCtlCommand`.
The given ``timeout`` and ``exception`` when instantiation will be used
to call :py:mod:`ryu.lib.ovs.vsctl.VSCtl.run_command`.
"""
self.vsctl.run_command(commands, self.timeout, self.exception) self.vsctl.run_command(commands, self.timeout, self.exception)
def init(self): def init(self):
"""
Validates the given ``ovsdb_addr`` and connects to OVS instance.
If failed to connect to OVS instance or the given ``datapath_id`` does
not match with the Datapath ID of the connected OVS instance, raises
:py:mod:`ryu.lib.ovs.bridge.OVSBridgeNotFound` exception.
"""
if not valid_ovsdb_addr(self.ovsdb_addr): if not valid_ovsdb_addr(self.ovsdb_addr):
raise ValueError('Invalid OVSDB address: %s' % self.ovsdb_addr) raise ValueError('Invalid OVSDB address: %s' % self.ovsdb_addr)
if self.br_name is None: if self.br_name is None:
@ -126,16 +162,37 @@ class OVSBridge(object):
return command.result[0].name return command.result[0].name
def get_controller(self): def get_controller(self):
"""
Gets the configured OpenFlow controller address.
This method is corresponding to the following ovs-vsctl command::
$ ovs-vsctl get-controller <bridge>
"""
command = ovs_vsctl.VSCtlCommand('get-controller', [self.br_name]) command = ovs_vsctl.VSCtlCommand('get-controller', [self.br_name])
self.run_command([command]) self.run_command([command])
return command.result[0] return command.result[0]
def set_controller(self, controllers): def set_controller(self, controllers):
"""
Sets the OpenFlow controller address.
This method is corresponding to the following ovs-vsctl command::
$ ovs-vsctl set-controller <bridge> <target>...
"""
command = ovs_vsctl.VSCtlCommand('set-controller', [self.br_name]) command = ovs_vsctl.VSCtlCommand('set-controller', [self.br_name])
command.args.extend(controllers) command.args.extend(controllers)
self.run_command([command]) self.run_command([command])
def del_controller(self): def del_controller(self):
"""
Deletes the configured OpenFlow controller address.
This method is corresponding to the following ovs-vsctl command::
$ ovs-vsctl del-controller <bridge>
"""
command = ovs_vsctl.VSCtlCommand('del-controller', [self.br_name]) command = ovs_vsctl.VSCtlCommand('del-controller', [self.br_name])
self.run_command([command]) self.run_command([command])
@ -245,30 +302,72 @@ class OVSBridge(object):
self.run_command([command]) self.run_command([command])
def db_get_val(self, table, record, column): def db_get_val(self, table, record, column):
"""
Gets values of 'column' in 'record' in 'table'.
This method is corresponding to the following ovs-vsctl command::
$ ovs-vsctl get TBL REC COL
"""
command = ovs_vsctl.VSCtlCommand('get', (table, record, column)) command = ovs_vsctl.VSCtlCommand('get', (table, record, column))
self.run_command([command]) self.run_command([command])
assert len(command.result) == 1 assert len(command.result) == 1
return command.result[0] return command.result[0]
def db_get_map(self, table, record, column): def db_get_map(self, table, record, column):
"""
Gets dict type value of 'column' in 'record' in 'table'.
This method is corresponding to the following ovs-vsctl command::
$ ovs-vsctl get TBL REC COL
"""
val = self.db_get_val(table, record, column) val = self.db_get_val(table, record, column)
assert isinstance(val, dict) assert isinstance(val, dict)
return val return val
def get_datapath_id(self): def get_datapath_id(self):
"""
Gets Datapath ID of OVS instance.
This method is corresponding to the following ovs-vsctl command::
$ ovs-vsctl get Bridge <bridge> datapath_id
"""
return self.db_get_val('Bridge', self.br_name, 'datapath_id') return self.db_get_val('Bridge', self.br_name, 'datapath_id')
def delete_port(self, port_name): def delete_port(self, port_name):
"""
Deletes a port on the OVS instance.
This method is corresponding to the following ovs-vsctl command::
$ ovs-vsctl --if-exists del-port <bridge> <port>
"""
command = ovs_vsctl.VSCtlCommand( command = ovs_vsctl.VSCtlCommand(
'del-port', (self.br_name, port_name), '--if-exists') 'del-port', (self.br_name, port_name), '--if-exists')
self.run_command([command]) self.run_command([command])
def get_ofport(self, port_name): def get_ofport(self, port_name):
"""
Gets the OpenFlow port number.
This method is corresponding to the following ovs-vsctl command::
$ ovs-vsctl get Interface <port> ofport
"""
ofport_list = self.db_get_val('Interface', port_name, 'ofport') ofport_list = self.db_get_val('Interface', port_name, 'ofport')
assert len(ofport_list) == 1 assert len(ofport_list) == 1
return int(ofport_list[0]) return int(ofport_list[0])
def get_port_name_list(self): def get_port_name_list(self):
"""
Gets a list of all ports on OVS instance.
This method is corresponding to the following ovs-vsctl command::
$ ovs-vsctl list-ports <bridge>
"""
command = ovs_vsctl.VSCtlCommand('list-ports', (self.br_name, )) command = ovs_vsctl.VSCtlCommand('list-ports', (self.br_name, ))
self.run_command([command]) self.run_command([command])
return command.result return command.result
@ -297,6 +396,16 @@ class OVSBridge(object):
def add_tunnel_port(self, name, tunnel_type, remote_ip, def add_tunnel_port(self, name, tunnel_type, remote_ip,
local_ip=None, key=None, ofport=None): local_ip=None, key=None, ofport=None):
"""
Creates a tunnel port.
:param name: Port name to be created
:param tunnel_type: Type of tunnel (gre or vxlan)
:param remote_ip: Remote IP address of tunnel
:param local_ip: Local IP address of tunnel
:param key: Key of GRE or VNI of VxLAN
:param ofport: Requested OpenFlow port number
"""
options = 'remote_ip=%(remote_ip)s' % locals() options = 'remote_ip=%(remote_ip)s' % locals()
if key: if key:
options += ',key=%(key)s' % locals() options += ',key=%(key)s' % locals()
@ -314,15 +423,32 @@ class OVSBridge(object):
def add_gre_port(self, name, remote_ip, def add_gre_port(self, name, remote_ip,
local_ip=None, key=None, ofport=None): local_ip=None, key=None, ofport=None):
"""
Creates a GRE tunnel port.
See the description of ``add_tunnel_port()``.
"""
self.add_tunnel_port(name, 'gre', remote_ip, self.add_tunnel_port(name, 'gre', remote_ip,
local_ip=local_ip, key=key, ofport=ofport) local_ip=local_ip, key=key, ofport=ofport)
def add_vxlan_port(self, name, remote_ip, def add_vxlan_port(self, name, remote_ip,
local_ip=None, key=None, ofport=None): local_ip=None, key=None, ofport=None):
"""
Creates a VxLAN tunnel port.
See the description of ``add_tunnel_port()``.
"""
self.add_tunnel_port(name, 'vxlan', remote_ip, self.add_tunnel_port(name, 'vxlan', remote_ip,
local_ip=local_ip, key=key, ofport=ofport) local_ip=local_ip, key=key, ofport=ofport)
def del_port(self, port_name): def del_port(self, port_name):
"""
Deletes a port on OVS instance.
This method is corresponding to the following ovs-vsctl command::
$ ovs-vsctl del-port <bridge> <port>
"""
command = ovs_vsctl.VSCtlCommand('del-port', (self.br_name, port_name)) command = ovs_vsctl.VSCtlCommand('del-port', (self.br_name, port_name))
self.run_command([command]) self.run_command([command])
@ -396,6 +522,9 @@ class OVSBridge(object):
return None return None
def set_qos(self, port_name, type='linux-htb', max_rate=None, queues=None): def set_qos(self, port_name, type='linux-htb', max_rate=None, queues=None):
"""
Sets a Qos rule and creates Queues on the given port.
"""
queues = queues if queues else [] queues = queues if queues else []
command_qos = ovs_vsctl.VSCtlCommand( command_qos = ovs_vsctl.VSCtlCommand(
'set-qos', 'set-qos',
@ -409,6 +538,9 @@ class OVSBridge(object):
return None return None
def del_qos(self, port_name): def del_qos(self, port_name):
"""
Deletes the Qos rule on the given port.
"""
command = ovs_vsctl.VSCtlCommand( command = ovs_vsctl.VSCtlCommand(
'del-qos', 'del-qos',
[port_name]) [port_name])

View File

@ -14,6 +14,9 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
"""
``ovs-vsctl`` command like library to speak OVSDB protocol
"""
from __future__ import print_function from __future__ import print_function
@ -53,9 +56,9 @@ def valid_ovsdb_addr(addr):
The valid formats are: The valid formats are:
- unix:file - ``unix:file``
- tcp:ip:port - ``tcp:ip:port``
- ssl:ip:port - ``ssl:ip:port``
If ip is IPv6 address, wrap ip with brackets (e.g., ssl:[::1]:6640). If ip is IPv6 address, wrap ip with brackets (e.g., ssl:[::1]:6640).
@ -959,6 +962,34 @@ class _VSCtlTable(object):
class VSCtlCommand(StringifyMixin): class VSCtlCommand(StringifyMixin):
"""
Class to describe artgumens similar to those of ``ovs-vsctl`` command.
``command`` specifies the command of ``ovs-vsctl``.
``args`` specifies a list or tuple of arguments for the given command.
``options`` specifies a list or tuple of options for the given command.
Please note that NOT all options of ``ovs-vsctl`` are supported.
For example, ``--id`` option is not yet supported.
This class supports the followings.
================= =========================================================
Option Description
================= =========================================================
``--may-exist`` Does nothing when the given port already exists.
The supported commands are ``add-port`` and
``add-bond``.
``--fake-iface`` Creates a port as a fake interface.
The supported command is ``add-bond``.
``--must-exist`` Raises exception if the given port does not exist.
The supported command is ``del-port``.
``--with-iface`` Takes effect to the interface which has the same name.
The supported command is ``del-port``.
``--if-exists`` Ignores exception when not found.
The supported command is ``get``.
================= =========================================================
"""
def __init__(self, command, args=None, options=None): def __init__(self, command, args=None, options=None):
super(VSCtlCommand, self).__init__() super(VSCtlCommand, self).__init__()
@ -978,6 +1009,13 @@ class VSCtlCommand(StringifyMixin):
class VSCtl(object): class VSCtl(object):
"""
A class to describe an Open vSwitch instance.
``remote`` specifies the address of the OVS instance.
:py:mod:`ryu.lib.ovs.vsctl.valid_ovsdb_addr` is a convenient function to
validate this address.
"""
def _reset(self): def _reset(self):
self.schema_helper = None self.schema_helper = None
@ -1237,6 +1275,19 @@ class VSCtl(object):
self._do_main(commands) self._do_main(commands)
def run_command(self, commands, timeout_sec=None, exception=None): def run_command(self, commands, timeout_sec=None, exception=None):
"""
Executes the given commands and sends OVSDB messages.
``commands`` must be a list of
:py:mod:`ryu.lib.ovs.vsctl.VSCtlCommand`.
If ``timeout_sec`` is specified, raises exception after the given
timeout [sec]. Additionally, if ``exception`` is specified, this
function will wraps exception using the given exception class.
Retruns ``None`` but fills ``result`` attribute for each command
instance.
"""
if timeout_sec is None: if timeout_sec is None:
self._run_command(commands) self._run_command(commands)
else: else: