diff options
-rw-r--r-- | doc/source/library.rst | 1 | ||||
-rw-r--r-- | doc/source/library_ovsdb.rst | 76 | ||||
-rw-r--r-- | ryu/lib/ovs/bridge.py | 134 | ||||
-rw-r--r-- | ryu/lib/ovs/vsctl.py | 57 |
4 files changed, 264 insertions, 4 deletions
diff --git a/doc/source/library.rst b/doc/source/library.rst index ff280415..cdaed768 100644 --- a/doc/source/library.rst +++ b/doc/source/library.rst @@ -15,3 +15,4 @@ Ryu provides some useful library for your network applications. library_bgp_speaker_ref.rst library_mrt.rst library_ovsdb_manager.rst + library_ovsdb.rst diff --git a/doc/source/library_ovsdb.rst b/doc/source/library_ovsdb.rst new file mode 100644 index 00000000..df7d1708 --- /dev/null +++ b/doc/source/library_ovsdb.rst @@ -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: diff --git a/ryu/lib/ovs/bridge.py b/ryu/lib/ovs/bridge.py index e48052fb..9753fa29 100644 --- a/ryu/lib/ovs/bridge.py +++ b/ryu/lib/ovs/bridge.py @@ -15,7 +15,7 @@ # limitations under the License. """ -slimmed down version of OVSBridge in quantum agent +Wrapper utility library of :py:mod:`ryu.lib.ovs.vsctl` """ import functools @@ -92,6 +92,26 @@ class TunnelPort(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, exception=None): @@ -105,9 +125,25 @@ class OVSBridge(object): self.br_name = None 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) 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): raise ValueError('Invalid OVSDB address: %s' % self.ovsdb_addr) if self.br_name is None: @@ -126,16 +162,37 @@ class OVSBridge(object): return command.result[0].name 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]) self.run_command([command]) return command.result[0] 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.args.extend(controllers) self.run_command([command]) 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]) self.run_command([command]) @@ -245,30 +302,72 @@ class OVSBridge(object): self.run_command([command]) 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)) self.run_command([command]) assert len(command.result) == 1 return command.result[0] 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) assert isinstance(val, dict) return val 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') 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( 'del-port', (self.br_name, port_name), '--if-exists') self.run_command([command]) 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') assert len(ofport_list) == 1 return int(ofport_list[0]) 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, )) self.run_command([command]) return command.result @@ -297,6 +396,16 @@ class OVSBridge(object): def add_tunnel_port(self, name, tunnel_type, remote_ip, 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() if key: options += ',key=%(key)s' % locals() @@ -314,15 +423,32 @@ class OVSBridge(object): def add_gre_port(self, name, remote_ip, 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, local_ip=local_ip, key=key, ofport=ofport) def add_vxlan_port(self, name, remote_ip, 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, local_ip=local_ip, key=key, ofport=ofport) 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)) self.run_command([command]) @@ -396,6 +522,9 @@ class OVSBridge(object): return 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 [] command_qos = ovs_vsctl.VSCtlCommand( 'set-qos', @@ -409,6 +538,9 @@ class OVSBridge(object): return None def del_qos(self, port_name): + """ + Deletes the Qos rule on the given port. + """ command = ovs_vsctl.VSCtlCommand( 'del-qos', [port_name]) diff --git a/ryu/lib/ovs/vsctl.py b/ryu/lib/ovs/vsctl.py index b18b5ef1..f3355a1c 100644 --- a/ryu/lib/ovs/vsctl.py +++ b/ryu/lib/ovs/vsctl.py @@ -14,6 +14,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +""" +``ovs-vsctl`` command like library to speak OVSDB protocol +""" from __future__ import print_function @@ -53,9 +56,9 @@ def valid_ovsdb_addr(addr): The valid formats are: - - unix:file - - tcp:ip:port - - ssl:ip:port + - ``unix:file`` + - ``tcp:ip:port`` + - ``ssl:ip:port`` 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 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): super(VSCtlCommand, self).__init__() @@ -978,6 +1009,13 @@ class VSCtlCommand(StringifyMixin): 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): self.schema_helper = None @@ -1237,6 +1275,19 @@ class VSCtl(object): self._do_main(commands) 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: self._run_command(commands) else: |