Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-ovsdbapp for openSUSE:Factory
checked in at 2024-01-05 21:41:38
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-ovsdbapp (Old)
and /work/SRC/openSUSE:Factory/.python-ovsdbapp.new.28375 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-ovsdbapp"
Fri Jan 5 21:41:38 2024 rev:15 rq:1136724 version:2.5.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-ovsdbapp/python-ovsdbapp.changes
2023-06-22 23:25:13.929623435 +0200
+++
/work/SRC/openSUSE:Factory/.python-ovsdbapp.new.28375/python-ovsdbapp.changes
2024-01-05 21:42:46.933283270 +0100
@@ -1,0 +2,15 @@
+Thu Jan 4 09:07:26 UTC 2024 - [email protected]
+
+- update to version 2.5.0
+ - Update master for stable/2023.2
+ - TAAS: Add commands for creating Mirrors
+ - Add support for ecmp routes
+ - Ensure LrpAddCommand may_exits works without peers
+ - Add some basic usage documentation
+ - Handle events with conditions and match_fn
+ - nb: provide 'route_table' in lr-route-{add,del,list}
+ - nb: allow to add route even if learned route exists
+ - venv: unit tests don't work for 'hardware_vtep'
+ - nb: add 'nexthop' argument to 'lr_route_del'
+
+-------------------------------------------------------------------
Old:
----
ovsdbapp-2.3.0.tar.gz
New:
----
ovsdbapp-2.5.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-ovsdbapp.spec ++++++
--- /var/tmp/diff_new_pack.09bHqS/_old 2024-01-05 21:42:47.473303016 +0100
+++ /var/tmp/diff_new_pack.09bHqS/_new 2024-01-05 21:42:47.473303016 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-ovsdbapp
#
-# Copyright (c) 2023 SUSE LLC
+# Copyright (c) 2024 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,13 +18,13 @@
%define with_tests 1
Name: python-ovsdbapp
-Version: 2.3.0
+Version: 2.5.0
Release: 0
Summary: A library for creating OVSDB applications
License: Apache-2.0
Group: Development/Languages/Python
URL: https://docs.openstack.org/ovsdbapp
-Source0:
https://files.pythonhosted.org/packages/source/o/ovsdbapp/ovsdbapp-2.3.0.tar.gz
+Source0:
https://files.pythonhosted.org/packages/source/o/ovsdbapp/ovsdbapp-2.5.0.tar.gz
BuildRequires: openstack-macros
BuildRequires: python3-fixtures >= 3.0.0
BuildRequires: python3-netaddr >= 0.7.18
@@ -70,7 +70,7 @@
%{py3_build}
# generate html docs
-PBR_VERSION=2.3.0 PYTHONPATH=. \
+PBR_VERSION=2.5.0 PYTHONPATH=. \
%sphinx_build -b html doc/source doc/build/html
rm -rf doc/build/html/.{doctrees,buildinfo}
++++++ _service ++++++
--- /var/tmp/diff_new_pack.09bHqS/_old 2024-01-05 21:42:47.501304040 +0100
+++ /var/tmp/diff_new_pack.09bHqS/_new 2024-01-05 21:42:47.501304040 +0100
@@ -1,13 +1,13 @@
<services>
- <service mode="disabled" name="renderspec">
+ <service mode="manual" name="renderspec">
<param
name="input-template">https://opendev.org/openstack/rpm-packaging/raw/master/openstack/ovsdbapp/ovsdbapp.spec.j2</param>
<param name="output-name">python-ovsdbapp.spec</param>
<param
name="requirements">https://opendev.org/openstack/ovsdbapp/raw/master/requirements.txt</param>
<param name="changelog-email">[email protected]</param>
<param name="changelog-provider">gh,openstack,ovsdbapp</param>
</service>
- <service mode="disabled" name="download_files">
+ <service mode="manual" name="download_files">
</service>
- <service name="format_spec_file" mode="disabled"/>
+ <service name="format_spec_file" mode="manual"/>
</services>
++++++ ovsdbapp-2.3.0.tar.gz -> ovsdbapp-2.5.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ovsdbapp-2.3.0/AUTHORS new/ovsdbapp-2.5.0/AUTHORS
--- old/ovsdbapp-2.3.0/AUTHORS 2023-04-27 17:14:39.000000000 +0200
+++ new/ovsdbapp-2.5.0/AUTHORS 2023-11-21 10:22:19.000000000 +0100
@@ -71,6 +71,7 @@
Yunxiang Tao <[email protected]>
caoyuan <[email protected]>
chenxing <[email protected]>
+elajkat <[email protected]>
gengchc2 <[email protected]>
hgangwx <[email protected]>
huang.zhiping <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ovsdbapp-2.3.0/ChangeLog new/ovsdbapp-2.5.0/ChangeLog
--- old/ovsdbapp-2.3.0/ChangeLog 2023-04-27 17:14:39.000000000 +0200
+++ new/ovsdbapp-2.5.0/ChangeLog 2023-11-21 10:22:19.000000000 +0100
@@ -1,6 +1,28 @@
CHANGES
=======
+2.5.0
+-----
+
+* nb: allow to add route even if learned route exists
+* TAAS: Add commands for creating Mirrors
+* nb: provide 'route\_table' in lr-route-{add,del,list}
+* nb: add 'nexthop' argument to 'lr\_route\_del'
+* Handle events with conditions and match\_fn
+* venv: unit tests don't work for 'hardware\_vtep'
+* Update master for stable/2023.2
+
+2.4.1
+-----
+
+* Add some basic usage documentation
+
+2.4.0
+-----
+
+* Ensure LrpAddCommand may\_exits works without peers
+* Add support for ecmp routes
+
2.3.0
-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ovsdbapp-2.3.0/PKG-INFO new/ovsdbapp-2.5.0/PKG-INFO
--- old/ovsdbapp-2.3.0/PKG-INFO 2023-04-27 17:14:39.963996200 +0200
+++ new/ovsdbapp-2.5.0/PKG-INFO 2023-11-21 10:22:19.864071600 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: ovsdbapp
-Version: 2.3.0
+Version: 2.5.0
Summary: A library for creating OVSDB applications
Home-page: https://pypi.org/project/ovsdbapp/
Author: OpenStack
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ovsdbapp-2.3.0/doc/source/user/index.rst
new/ovsdbapp-2.5.0/doc/source/user/index.rst
--- old/ovsdbapp-2.3.0/doc/source/user/index.rst 2023-04-27
17:14:09.000000000 +0200
+++ new/ovsdbapp-2.5.0/doc/source/user/index.rst 2023-11-21
10:21:52.000000000 +0100
@@ -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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ovsdbapp-2.3.0/doc/source/user/overview.rst
new/ovsdbapp-2.5.0/doc/source/user/overview.rst
--- old/ovsdbapp-2.3.0/doc/source/user/overview.rst 1970-01-01
01:00:00.000000000 +0100
+++ new/ovsdbapp-2.5.0/doc/source/user/overview.rst 2023-11-21
10:21:52.000000000 +0100
@@ -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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ovsdbapp-2.3.0/doc/source/user/tutorial.rst
new/ovsdbapp-2.5.0/doc/source/user/tutorial.rst
--- old/ovsdbapp-2.3.0/doc/source/user/tutorial.rst 1970-01-01
01:00:00.000000000 +0100
+++ new/ovsdbapp-2.5.0/doc/source/user/tutorial.rst 2023-11-21
10:21:52.000000000 +0100
@@ -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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ovsdbapp-2.3.0/ovsdbapp/backend/ovs_idl/event.py
new/ovsdbapp-2.5.0/ovsdbapp/backend/ovs_idl/event.py
--- old/ovsdbapp-2.3.0/ovsdbapp/backend/ovs_idl/event.py 2023-04-27
17:14:09.000000000 +0200
+++ new/ovsdbapp-2.5.0/ovsdbapp/backend/ovs_idl/event.py 2023-11-21
10:21:52.000000000 +0100
@@ -20,6 +20,16 @@
class RowEvent(ovsdb_event.RowEvent): # pylint: disable=abstract-method
def match_fn(self, event, row, old):
+ """User-overridable custom matching function
+
+ This method takes the same arguments as the RowEvent API call
+ `matches` and allows for more complex matching criteria. This
+ method will apply additional checks to those specified in the
+ creation of the RowEvent
+ """
+ return True
+
+ def base_match(self, event, row, old):
if self.conditions and not idlutils.row_match(row, self.conditions):
return False
if self.old_conditions:
@@ -38,6 +48,8 @@
return False
if row._table.name != self.table:
return False
+ if not self.base_match(event, row, old):
+ return False
if not self.match_fn(event, row, old):
return False
LOG.debug("Matched %s: %r to row=%s old=%s", event.upper(), self,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ovsdbapp-2.3.0/ovsdbapp/constants.py
new/ovsdbapp-2.5.0/ovsdbapp/constants.py
--- old/ovsdbapp-2.3.0/ovsdbapp/constants.py 2023-04-27 17:14:09.000000000
+0200
+++ new/ovsdbapp-2.5.0/ovsdbapp/constants.py 2023-11-21 10:21:52.000000000
+0100
@@ -39,5 +39,6 @@
PROTO_UDP = 'udp'
ROUTE_DISCARD = "discard"
+MAIN_ROUTE_TABLE = ""
LOCALNET = 'localnet'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ovsdbapp-2.3.0/ovsdbapp/schema/ovn_northbound/api.py
new/ovsdbapp-2.5.0/ovsdbapp/schema/ovn_northbound/api.py
--- old/ovsdbapp-2.3.0/ovsdbapp/schema/ovn_northbound/api.py 2023-04-27
17:14:09.000000000 +0200
+++ new/ovsdbapp-2.5.0/ovsdbapp/schema/ovn_northbound/api.py 2023-11-21
10:21:52.000000000 +0100
@@ -735,7 +735,8 @@
@abc.abstractmethod
def lr_route_add(self, router, prefix, nexthop, port=None,
- policy='dst-ip', may_exist=False):
+ policy='dst-ip', may_exist=False, ecmp=False,
+ route_table=const.MAIN_ROUTE_TABLE):
"""Add a route to 'router'
:param router: The name or uuid of the router
@@ -754,11 +755,18 @@
:type policy: string, 'dst-ip' or 'src-ip'
:param may_exist: If True, don't fail if the route already exists
:type may_exist: boolean
+ :param ecmp: Enable ECMP support. If True adding routes with
+ same IP prefix is allowed as long as the nexthop is
+ different
+ :type ecmp: boolean
+ :param route_table: The name of route table
+ :type route_table: str
returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
- def lr_route_del(self, router, prefix=None, if_exists=False):
+ def lr_route_del(self, router, prefix=None, if_exists=False, nexthop=None,
+ route_table=const.MAIN_ROUTE_TABLE):
"""Remove routes from 'router'
:param router: The name or uuid of the router
@@ -767,16 +775,25 @@
:type prefix: type string
:param if_exists: If True, don't fail if the port doesn't exist
:type if_exists: boolean
+ :parm nexthop: The gateway to use for this route, which should be
+ the IP address of one of `router`'s logical router
+ ports or the IP address of a logical port
+ :type nexthop: string
+ :param route_table: The name of route table
+ :type route_table: str
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
- def lr_route_list(self, router):
+ def lr_route_list(self, router, route_table=None):
"""Get the UUIDs of static logical routes from 'router'
- :param router: The name or uuid of the router
- :type router: string or uuid.UUID
- :returns: :class:`Command` with RowView list result
+ :param router: The name or uuid of the router
+ :type router: string or uuid.UUID
+ :param route_table: The name of route table. Pass "" to get routes of
+ global route table only
+ :type route_table: str
+ :returns: :class:`Command` with RowView list result
"""
@abc.abstractmethod
@@ -1466,3 +1483,71 @@
:type uuid: uuid.UUID
:returns: :class:`Command` with RowView result
"""
+
+ @abc.abstractmethod
+ def mirror_get(self, uuid):
+ """Get the Mirror entry"""
+
+ @abc.abstractmethod
+ def mirror_del(self, mirror):
+ """Delete a Mirror"""
+
+ @abc.abstractmethod
+ def mirror_add(self, name, mirror_type, index, direction_filter, dest,
+ external_ids=None,
+ may_exist=False):
+ """Create a Mirror entry
+
+ :param name: Name of the Mirror to create.
+ :type name: str
+ :param mirror_type: The type of the mirroring can be gre or erspan.
+ :type mirror_type: str
+ :param index: The index filed will be used for the Index field in
+ ERSPAN header as decimal, and as hexadecimal value in
+ the SpanID field, for GRE mirrors it will be the Key
+ field.
+ :type index: int
+ :param direction_filter: The direction of the traffic to be mirrored,
+ can be from-lport and to-lprt.
+ :type direction_filter: str
+ :param dest: The destination IP address of the mirroring.
+ :type dest: str
+
+
+ :param external_ids: Values to be added as external_id pairs.
+ :type external_ids: Optional[Dict[str,str]]
+ :param may_exist: If True, update any existing Mirror entry if it
+ already exists. Default is False which will raise
+ an error if a Mirror entry with same logical_port,
+ sink pair already exists.
+ :type may_exist: Optional[bool]
+ :returns: :class:`Command` with RowView result
+ """
+
+ @abc.abstractmethod
+ def lsp_attach_mirror(self, port, mirror, may_exist=False):
+ """Attaches an lsp to the given mirror
+
+ :param port: the id of the lsp
+ :type port: str
+ :param mirror: the name or ID of the mirror.
+ :type mirror: str
+ :param may_exist: If True, don't fail if the mirror_rule already
+ exists.
+ :type may_exist: Optional[bool]
+ :returns: :class:`Command` with RowView result
+ """
+
+ @abc.abstractmethod
+ def lsp_detach_mirror(self, port, mirror, if_exist=False):
+ """Detaches an lsp from the given mirror
+
+ :param port: the id of the lsp
+ :type port: str
+ :param mirror: the name or ID of the mirror
+ :type mirror: str
+ :param if_exist: If True, don't fail if the mirror_rules entry
+ doesn't exist.
+ :type if_exist: Optional[bool]
+ :returns: :class:`Command` with RowView result
+ """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/ovsdbapp-2.3.0/ovsdbapp/schema/ovn_northbound/commands.py
new/ovsdbapp-2.5.0/ovsdbapp/schema/ovn_northbound/commands.py
--- old/ovsdbapp-2.3.0/ovsdbapp/schema/ovn_northbound/commands.py
2023-04-27 17:14:09.000000000 +0200
+++ new/ovsdbapp-2.5.0/ovsdbapp/schema/ovn_northbound/commands.py
2023-11-21 10:21:52.000000000 +0100
@@ -853,7 +853,7 @@
self.networks = [str(netaddr.IPNetwork(net)) for net in networks]
self.router = router
self.port = port
- self.peer = peer
+ self.peer = peer if peer else []
self.may_exist = may_exist
self.columns = columns
super().__init__(api)
@@ -1137,9 +1137,117 @@
table = 'BFD'
+class MirrorAddCommand(cmd.AddCommand):
+ # cmd.AddCommand uses self.table_name, other base commands use self.table
+ table_name = 'Mirror'
+
+ def __init__(self, api, name, mirror_type, index, direction_filter, dest,
+ external_ids=None, may_exist=False):
+ self.name = name
+ self.direction_filter = direction_filter
+ self.mirror_type = mirror_type
+ if mirror_type != 'local':
+ self.dest = str(netaddr.IPAddress(dest))
+ else:
+ self.dest = dest
+ self.index = index
+
+ super().__init__(api)
+
+ self.columns = {
+ 'name': name,
+ 'filter': direction_filter,
+ 'sink': dest,
+ 'type': mirror_type,
+ 'index': index,
+ 'external_ids': external_ids or {},
+ }
+ self.may_exist = may_exist
+
+ def run_idl(self, txn):
+ try:
+ mirror_result = self.api.lookup(self.table_name, self.name)
+ self.result = rowview.RowView(mirror_result)
+ if self.may_exist:
+ self.set_columns(mirror_result, **self.columns)
+ self.result = rowview.RowView(mirror_result)
+ return
+ raise RuntimeError("Mirror %s already exists" % self.name)
+ except idlutils.RowNotFound:
+ pass
+
+ mirror = txn.insert(self.api.tables[self.table_name])
+ mirror.name = self.name
+ mirror.type = self.mirror_type
+ mirror.filter = self.direction_filter
+ mirror.sink = self.dest
+ mirror.index = self.index
+ self.set_columns(mirror, **self.columns)
+ # Setting the result to something other than a :class:`rowview.RowView`
+ # or :class:`ovs.db.idl.Row` typed value will make the parent
+ # `post_commit` method retrieve the newly insterted row from IDL and
+ # return that to the caller.
+ self.result = mirror.uuid
+
+
+class MirrorDelCommand(cmd.DbDestroyCommand):
+ table = 'Mirror'
+
+ def __init__(self, api, record):
+ super().__init__(api, self.table, record)
+
+
+class MirrorGetCommand(cmd.BaseGetRowCommand):
+ table = 'Mirror'
+
+
+class LspAttachMirror(cmd.BaseCommand):
+ def __init__(self, api, port, mirror, may_exist=False):
+ super().__init__(api)
+ self.port = port
+ self.mirror = mirror
+ self.may_exist = may_exist
+
+ def run_idl(self, txn):
+ try:
+ lsp = self.api.lookup('Logical_Switch_Port', self.port)
+ mirror = self.api.lookup('Mirror', self.mirror)
+ if mirror in lsp.mirror_rules and not self.may_exist:
+ msg = "Mirror Rule %s is already set on LSP %s" % (self.mirror,
+ self.port)
+ raise RuntimeError(msg)
+ lsp.addvalue('mirror_rules', self.mirror)
+ except idlutils.RowNotFound as e:
+ raise RuntimeError("LSP %s not found" % self.port) from e
+
+
+class LspDetachMirror(cmd.BaseCommand):
+ def __init__(self, api, port, mirror, if_exist=False):
+ super().__init__(api)
+ self.port = port
+ self.mirror = mirror
+ self.if_exist = if_exist
+
+ def run_idl(self, txn):
+ try:
+ lsp = self.api.lookup('Logical_Switch_Port', self.port)
+ mirror = self.api.lookup('Mirror', self.mirror)
+ if mirror not in lsp.mirror_rules and not self.if_exist:
+ msg = "Mirror Rule %s doesn't exist on LSP %s" % (self.mirror,
+ self.port)
+ raise RuntimeError(msg)
+ lsp.delvalue('mirror_rules', self.mirror)
+ except idlutils.RowNotFound as e:
+ if self.if_exists:
+ return
+ msg = "LSP %s doesn't exist" % self.port
+ raise RuntimeError(msg) from e
+
+
class LrRouteAddCommand(cmd.BaseCommand):
def __init__(self, api, router, prefix, nexthop, port=None,
- policy='dst-ip', may_exist=False):
+ policy='dst-ip', may_exist=False, ecmp=False,
+ route_table=const.MAIN_ROUTE_TABLE):
prefix = str(netaddr.IPNetwork(prefix))
if nexthop != const.ROUTE_DISCARD:
nexthop = str(netaddr.IPAddress(nexthop))
@@ -1149,12 +1257,20 @@
self.nexthop = nexthop
self.port = port
self.policy = policy
+ self.ecmp = ecmp
+ self.route_table = route_table
self.may_exist = may_exist
def run_idl(self, txn):
lr = self.api.lookup('Logical_Router', self.router)
for route in lr.static_routes:
- if self.prefix == route.ip_prefix:
+ if (
+ self.prefix == route.ip_prefix and
+ self.route_table == route.route_table and
+ "ic-learned-route" not in route.external_ids
+ ):
+ if self.ecmp and self.nexthop != route.nexthop:
+ continue
if not self.may_exist:
msg = "Route %s already exists on router %s" % (
self.prefix, self.router)
@@ -1169,6 +1285,7 @@
route.ip_prefix = self.prefix
route.nexthop = self.nexthop
route.policy = self.policy
+ route.route_table = self.route_table
if self.port:
route.output_port = self.port
lr.addvalue('static_routes', route)
@@ -1183,12 +1300,15 @@
class LrRouteDelCommand(cmd.BaseCommand):
- def __init__(self, api, router, prefix=None, if_exists=False):
+ def __init__(self, api, router, prefix=None, if_exists=False,
+ nexthop=None, route_table=const.MAIN_ROUTE_TABLE):
if prefix is not None:
prefix = str(netaddr.IPNetwork(prefix))
super().__init__(api)
self.router = router
self.prefix = prefix
+ self.nexthop = nexthop
+ self.route_table = route_table
self.if_exists = if_exists
def run_idl(self, txn):
@@ -1197,9 +1317,14 @@
lr.static_routes = []
return
for route in lr.static_routes:
- if self.prefix == route.ip_prefix:
+ if (
+ self.prefix == route.ip_prefix and
+ self.route_table == route.route_table
+ ):
+ if self.nexthop and route.nexthop != self.nexthop:
+ continue
+
lr.delvalue('static_routes', route)
- # There should only be one possible match
return
if not self.if_exists:
@@ -1209,13 +1334,19 @@
class LrRouteListCommand(cmd.ReadOnlyCommand):
- def __init__(self, api, router):
+ def __init__(self, api, router, route_table=None):
super().__init__(api)
self.router = router
+ self.route_table = route_table
def run_idl(self, txn):
lr = self.api.lookup('Logical_Router', self.router)
- self.result = [rowview.RowView(r) for r in lr.static_routes]
+ if self.route_table is not None:
+ self.result = [rowview.RowView(r)
+ for r in lr.static_routes
+ if r.route_table == self.route_table]
+ else:
+ self.result = [rowview.RowView(r) for r in lr.static_routes]
class LrNatAddCommand(cmd.BaseCommand):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/ovsdbapp-2.3.0/ovsdbapp/schema/ovn_northbound/impl_idl.py
new/ovsdbapp-2.5.0/ovsdbapp/schema/ovn_northbound/impl_idl.py
--- old/ovsdbapp-2.3.0/ovsdbapp/schema/ovn_northbound/impl_idl.py
2023-04-27 17:14:09.000000000 +0200
+++ new/ovsdbapp-2.5.0/ovsdbapp/schema/ovn_northbound/impl_idl.py
2023-11-21 10:21:52.000000000 +0100
@@ -236,15 +236,18 @@
return cmd.LrpDelNetworksCommand(self, port, networks, if_exists)
def lr_route_add(self, router, prefix, nexthop, port=None,
- policy='dst-ip', may_exist=False):
+ policy='dst-ip', may_exist=False, ecmp=False,
+ route_table=const.MAIN_ROUTE_TABLE):
return cmd.LrRouteAddCommand(self, router, prefix, nexthop, port,
- policy, may_exist)
+ policy, may_exist, ecmp, route_table)
- def lr_route_del(self, router, prefix=None, if_exists=False):
- return cmd.LrRouteDelCommand(self, router, prefix, if_exists)
+ def lr_route_del(self, router, prefix=None, if_exists=False, nexthop=None,
+ route_table=const.MAIN_ROUTE_TABLE):
+ return cmd.LrRouteDelCommand(self, router, prefix, if_exists, nexthop,
+ route_table)
- def lr_route_list(self, router):
- return cmd.LrRouteListCommand(self, router)
+ def lr_route_list(self, router, route_table=None):
+ return cmd.LrRouteListCommand(self, router, route_table)
def lr_nat_add(self, router, nat_type, external_ip, logical_ip,
logical_port=None, external_mac=None, may_exist=False):
@@ -432,3 +435,25 @@
def bfd_get(self, uuid):
return cmd.BFDGetCommand(self, uuid)
+
+ def mirror_get(self, uuid):
+ return cmd.MirrorGetCommand(self, uuid)
+
+ def mirror_del(self, mirror):
+ return cmd.MirrorDelCommand(self, mirror)
+
+ def mirror_add(self, name, mirror_type, index, direction_filter,
+ dest, external_ids=None, may_exist=False):
+ return cmd.MirrorAddCommand(self, name=name,
+ mirror_type=mirror_type,
+ index=index,
+ direction_filter=direction_filter,
+ dest=dest,
+ external_ids=external_ids,
+ may_exist=may_exist)
+
+ def lsp_attach_mirror(self, port, mirror, may_exist=False):
+ return cmd.LspAttachMirror(self, port, mirror, may_exist)
+
+ def lsp_detach_mirror(self, port, mirror, if_exist=False):
+ return cmd.LspDetachMirror(self, port, mirror, if_exist)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/ovsdbapp-2.3.0/ovsdbapp/tests/functional/schema/hardware_vtep/test_impl_idl.py
new/ovsdbapp-2.5.0/ovsdbapp/tests/functional/schema/hardware_vtep/test_impl_idl.py
---
old/ovsdbapp-2.3.0/ovsdbapp/tests/functional/schema/hardware_vtep/test_impl_idl.py
2023-04-27 17:14:09.000000000 +0200
+++
new/ovsdbapp-2.5.0/ovsdbapp/tests/functional/schema/hardware_vtep/test_impl_idl.py
2023-11-21 10:21:52.000000000 +0100
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import time
+
from ovsdbapp.backend.ovs_idl import idlutils
from ovsdbapp.schema.hardware_vtep.commands import get_global_record
from ovsdbapp.tests.functional import base
@@ -352,6 +354,16 @@
]:
self.addCleanup(self.ovsvenv.call, args)
+ def _wait_db_rows(self, table):
+ """Wait for rows in specified table. Raises RuntimeError otherwise."""
+
+ for _ in range(4):
+ if table.rows:
+ return
+ time.sleep(0.5)
+
+ raise RuntimeError("Table '%s' is empty" % table.name)
+
def test_list_local_macs(self):
local_macs = self.api.list_local_macs(
self.ls.name).execute(check_error=True)
@@ -370,6 +382,7 @@
ucast_table = self.api.tables['Ucast_Macs_Local']
mcast_table = self.api.tables['Mcast_Macs_Local']
for table in [ucast_table, mcast_table]:
+ self._wait_db_rows(table)
self.assertEqual(len(table.rows), 1)
self.api.clear_local_macs(self.ls.name).execute(check_error=True)
@@ -380,6 +393,7 @@
ucast_table = self.api.tables['Ucast_Macs_Remote']
mcast_table = self.api.tables['Mcast_Macs_Remote']
for table in [ucast_table, mcast_table]:
+ self._wait_db_rows(table)
self.assertEqual(len(table.rows), 1)
self.api.clear_remote_macs(self.ls.name).execute(check_error=True)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/ovsdbapp-2.3.0/ovsdbapp/tests/functional/schema/ovn_northbound/test_impl_idl.py
new/ovsdbapp-2.5.0/ovsdbapp/tests/functional/schema/ovn_northbound/test_impl_idl.py
---
old/ovsdbapp-2.3.0/ovsdbapp/tests/functional/schema/ovn_northbound/test_impl_idl.py
2023-04-27 17:14:09.000000000 +0200
+++
new/ovsdbapp-2.5.0/ovsdbapp/tests/functional/schema/ovn_northbound/test_impl_idl.py
2023-11-21 10:21:52.000000000 +0100
@@ -956,14 +956,18 @@
self.assertTrue(lrs.issubset(lr_set), "%s vs %s" % (lrs, lr_set))
def _lr_add_route(self, router=None, prefix=None, nexthop=None, port=None,
+ ecmp=False, route_table=const.MAIN_ROUTE_TABLE,
**kwargs):
lr = self._lr_add(router or utils.get_rand_device_name(),
may_exist=True)
prefix = prefix or '192.0.2.0/25'
nexthop = nexthop or '192.0.2.254'
port = port or "port_name"
- sr = self.api.lr_route_add(lr.uuid, prefix, nexthop, port,
- **kwargs).execute(check_error=True)
+ sr = self.api.lr_route_add(
+ lr.uuid, prefix, nexthop, port,
+ ecmp=ecmp, route_table=route_table,
+ **kwargs
+ ).execute(check_error=True)
self.assertIn(sr, lr.static_routes)
self.assertEqual(prefix, sr.ip_prefix)
self.assertEqual(nexthop, sr.nexthop)
@@ -992,11 +996,45 @@
self._lr_add_route(router_name)
self._lr_add_route(router_name, may_exist=True)
+ def test_lr_route_add_exists_ecmp(self):
+ router_name = utils.get_rand_device_name()
+ self._lr_add_route(router_name)
+ self._lr_add_route(router=router_name, nexthop='192.0.3.254',
+ ecmp=True)
+
def test_lr_route_add_discard(self):
self._lr_add_route(nexthop=const.ROUTE_DISCARD)
self.assertRaises(netaddr.AddrFormatError, self._lr_add_route,
prefix='not-discard')
+ def test_lr_route_add_route_table(self):
+ lr = self._lr_add()
+ route_table = "route-table"
+
+ # add route to 'main' route table
+ route = self._lr_add_route(lr.name)
+ self.assertEqual(route.route_table, const.MAIN_ROUTE_TABLE)
+
+ route = self._lr_add_route(lr.name, route_table=route_table)
+ self.assertEqual(route.route_table, route_table)
+
+ self.assertEqual(
+ len(self.api.tables['Logical_Router_Static_Route'].rows), 2)
+
+ def test_lr_route_add_learned_route_exist(self):
+ router_name = utils.get_rand_device_name()
+
+ learned_route = self._lr_add_route(router_name)
+ self.api.db_set(
+ 'Logical_Router_Static_Route', learned_route.uuid,
+ ('external_ids', {'ic-learned-route': str(uuid.uuid4())})).execute(
+ check_error=True)
+
+ route = self._lr_add_route(router_name)
+
+ self.assertNotEqual(learned_route.uuid, route.uuid)
+ self.assertNotIn("ic-learned-route", route.external_ids)
+
def test_lr_route_del(self):
prefix = "192.0.2.0/25"
route = self._lr_add_route(prefix=prefix)
@@ -1025,6 +1063,55 @@
self.api.lr_route_del(lr.uuid, '192.0.2.0/25', if_exists=True).execute(
check_error=True)
+ def test_lr_route_del_ecmp(self):
+ prefix = "10.0.0.0/24"
+ nexthop1 = "1.1.1.1"
+ nexthop2 = "2.2.2.2"
+ lr = self._lr_add()
+
+ self._lr_add_route(lr.uuid, prefix=prefix, nexthop=nexthop1)
+ ecmp_route = self._lr_add_route(lr.uuid, prefix=prefix,
+ nexthop=nexthop2, ecmp=True)
+ self.assertEqual(
+ len(self.api.tables['Logical_Router_Static_Route'].rows), 2)
+
+ self.api.lr_route_del(lr.uuid, prefix, nexthop=nexthop2).execute(
+ check_error=True)
+
+ self.assertNotIn(
+ ecmp_route.uuid,
+ len(self.api.tables['Logical_Router_Static_Route'].rows),
+ )
+
+ cmd = self.api.lr_route_del(lr.uuid, prefix, nexthop=nexthop2)
+ self.assertRaises(RuntimeError, cmd.execute, check_error=True)
+
+ self.assertEqual(
+ len(self.api.tables['Logical_Router_Static_Route'].rows), 1)
+
+ def test_lr_route_del_route_table(self):
+ lr = self._lr_add()
+ route_table = "route-table"
+
+ route_in_main = self._lr_add_route(lr.uuid, prefix="10.0.0.0/24")
+ route = self._lr_add_route(
+ lr.uuid, prefix="10.0.1.0/24", route_table=route_table)
+
+ self.assertEqual(len(lr.static_routes), 2)
+
+ # try to delete from the 'main' table implicitly
+ cmd = self.api.lr_route_del(lr.uuid, route.ip_prefix)
+ self.assertRaises(RuntimeError, cmd.execute, check_error=True)
+
+ self.api.lr_route_del(
+ lr.uuid, prefix=route.ip_prefix, route_table=route_table
+ ).execute(check_error=True)
+ self.assertEqual(len(lr.static_routes), 1)
+
+ self.api.lr_route_del(
+ lr.uuid, route_in_main.ip_prefix).execute(check_error=True)
+ self.assertEqual(len(lr.static_routes), 0)
+
def test_lr_route_list(self):
lr = self._lr_add()
routes = {self._lr_add_route(lr.uuid, prefix="192.0.%s.0/25" % p)
@@ -1033,6 +1120,29 @@
check_error=True))
self.assertTrue(routes.issubset(route_set))
+ def test_lr_route_list_route_table(self):
+ lr = self._lr_add()
+ route_table = "route-table"
+
+ prefix1 = "10.0.0.0/24"
+ prefix2 = "10.0.1.0/24"
+
+ self._lr_add_route(lr.uuid, prefix=prefix1)
+ self._lr_add_route(lr.uuid, prefix=prefix2, route_table=route_table)
+
+ routes = self.api.lr_route_list(lr.uuid).execute(check_error=True)
+ self.assertEqual(len(routes), 2) # all routes in logical router
+
+ for route_table, prefix in zip(
+ [const.MAIN_ROUTE_TABLE, route_table],
+ [prefix1, prefix2]
+ ):
+ routes = self.api.lr_route_list(
+ lr.uuid, route_table=route_table).execute(check_error=True)
+ self.assertEqual(len(routes), 1)
+ self.assertEqual(routes[0].ip_prefix, prefix)
+ self.assertEqual(routes[0].route_table, route_table)
+
def _lr_nat_add(self, *args, **kwargs):
lr = kwargs.pop('router', self._lr_add(utils.get_rand_device_name()))
nat = self.api.lr_nat_add(
@@ -1378,7 +1488,7 @@
def test_lrp_add(self):
self._lrp_add(None, 'de:ad:be:ef:4d:ad', ['192.0.2.0/24'])
- def test_lpr_add_peer(self):
+ def test_lrp_add_peer(self):
lrp = self._lrp_add(None, 'de:ad:be:ef:4d:ad', ['192.0.2.0/24'],
peer='fake_peer')
self.assertIn('fake_peer', lrp.peer)
@@ -1409,7 +1519,7 @@
name = utils.get_rand_device_name()
args = (name, 'de:ad:be:ef:4d:ad', ['192.0.2.0/24'])
self._lrp_add(*args)
- self.assertRaises(RuntimeError, self._lrp_add, *args, may_exist=True)
+ self._lrp_add(*args, may_exist=True)
def test_lrp_add_may_exist_different_router(self):
name = utils.get_rand_device_name()
@@ -2526,3 +2636,137 @@
b1 = self.api.bfd_add(name, name).execute(check_error=True)
b2 = self.api.bfd_get(b1.uuid).execute(check_error=True)
self.assertEqual(b1, b2)
+
+
+class TestMirrorOps(OvnNorthboundTest):
+
+ def setUp(self):
+ super(TestMirrorOps, self).setUp()
+ self.table = self.api.tables['Mirror']
+ self.switch = self.useFixture(
+ fixtures.LogicalSwitchFixture(self.api)).obj
+ lsp_add_cmd = self.api.lsp_add(self.switch.uuid, 'testport')
+ with self.api.transaction(check_error=True) as txn:
+ txn.add(lsp_add_cmd)
+
+ self.port_uuid = lsp_add_cmd.result.uuid
+
+ def _mirror_add(self, name=None, direction_filter='to-lport',
+ dest='10.11.1.1', mirror_type='gre', index=42, **kwargs):
+ if not name:
+ name = utils.get_rand_name()
+ cmd = self.api.mirror_add(name, mirror_type, index, direction_filter,
+ dest, **kwargs)
+ row = cmd.execute(check_error=True)
+ self.assertEqual(cmd.name, row.name)
+ self.assertEqual(cmd.direction_filter, row.filter)
+ self.assertEqual(cmd.dest, row.sink)
+ self.assertEqual(cmd.mirror_type, row.type)
+ self.assertEqual(cmd.index, row.index)
+ return idlutils.frozen_row(row)
+
+ def test_mirror_addx(self):
+ self._mirror_add(dest='10.13.1.1')
+
+ def test_mirror_add_duplicate(self):
+ name = utils.get_rand_name()
+ cmd = self.api.mirror_add(name, 'gre', 100, 'from-lport',
+ '192.169.1.1')
+ cmd.execute(check_error=True)
+ self.assertRaises(RuntimeError, cmd.execute, check_error=True)
+
+ def test_mirror_add_may_exist_no_change(self):
+ name = utils.get_rand_name()
+ mirror1 = self._mirror_add(name=name, dest='10.18.1.1')
+ mirror2 = self._mirror_add(name=name, dest='10.18.1.1',
+ may_exist=True)
+ self.assertEqual(mirror1, mirror2)
+
+ def test_mirror_add_may_exist_change(self):
+ name = utils.get_rand_name()
+ mirror1 = self._mirror_add(name=name, dest='10.12.1.0')
+ mirror2 = self._mirror_add(
+ name=name, direction_filter='from-lport', dest='10.12.1.0',
+ mirror_type='gre', index=100, may_exist=True,
+ )
+ self.assertNotEqual(mirror1, mirror2)
+ self.assertEqual(mirror1.uuid, mirror2.uuid)
+
+ def test_mirror_del(self):
+ name = utils.get_rand_name()
+ mirror1 = self._mirror_add(name=name, dest='10.14.1.0')
+ self.assertIn(mirror1.uuid, self.table.rows)
+ self.api.mirror_del(mirror1.uuid).execute(check_error=True)
+ self.assertNotIn(mirror1.uuid, self.table.rows)
+
+ def test_mirror_get(self):
+ name = utils.get_rand_name()
+ mirror1 = self.api.mirror_add(name, 'gre', 100, 'from-lport',
+ '10.15.1.1').execute(check_error=True)
+ mirror2 = self.api.mirror_get(mirror1.uuid).execute(check_error=True)
+ self.assertEqual(mirror1, mirror2)
+
+ def test_lsp_attach_detach_mirror(self):
+ mirror = self._mirror_add(name='my_mirror')
+ self.api.lsp_attach_mirror(
+ self.port_uuid, mirror.uuid).execute(check_error=True)
+ port = self.api.lsp_get(self.port_uuid).execute(check_error=True)
+
+ self.assertEqual(1, len(port.mirror_rules))
+ mir_rule = self.api.lookup('Mirror', port.mirror_rules[0].uuid)
+ self.assertEqual(mirror.uuid, mir_rule.uuid)
+
+ self.api.lsp_detach_mirror(
+ self.port_uuid, mirror.uuid).execute(check_error=True)
+ port = self.api.lsp_get(self.port_uuid).execute(check_error=True)
+
+ self.assertEqual(0, len(port.mirror_rules))
+
+ def test_lsp_attach_detach_may_exist(self):
+ mirror1 = self._mirror_add(name='mirror1')
+ self.api.lsp_attach_mirror(
+ self.port_uuid, mirror1.uuid).execute(check_error=True)
+ mirror2 = self._mirror_add(name='mirror2', dest='10.17.1.0')
+
+ # Try to attach a mirror to a port which already has mirror_rule
+ # attached
+ failing_cmd = self.api.lsp_attach_mirror(
+ self.port_uuid, mirror1.uuid,
+ may_exist=False)
+ self.assertRaises(
+ RuntimeError,
+ failing_cmd.execute,
+ check_error=True
+ )
+
+ self.api.lsp_attach_mirror(
+ self.port_uuid, mirror2.uuid,
+ may_exist=True).execute(check_error=True)
+ check_res = self.api.lsp_get(self.port_uuid).execute(check_error=True)
+ rule_on_lsp = False
+ for m_rule in check_res.mirror_rules:
+ if mirror2.uuid == m_rule.uuid:
+ rule_on_lsp = True
+ self.assertTrue(rule_on_lsp)
+
+ self.api.lsp_detach_mirror(
+ self.port_uuid, mirror2.uuid).execute(check_error=True)
+ port = self.api.lsp_get(self.port_uuid).execute(check_error=True)
+ self.assertEqual(1, len(port.mirror_rules))
+ self.assertEqual(mirror1.uuid, port.mirror_rules[0].uuid)
+
+ # Try to detach a rule that is already detached
+ failing_cmd = self.api.lsp_detach_mirror(
+ self.port_uuid, mirror2.uuid)
+ self.assertRaises(
+ RuntimeError,
+ failing_cmd.execute,
+ check_error=True
+ )
+
+ # detach with if_exist=True, and check the result, to be as previously
+ self.api.lsp_detach_mirror(
+ self.port_uuid, mirror2.uuid,
+ if_exist=True).execute(check_error=True)
+ self.assertEqual(1, len(port.mirror_rules))
+ self.assertEqual(mirror1.uuid, port.mirror_rules[0].uuid)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/ovsdbapp-2.3.0/ovsdbapp/tests/functional/schema/ovn_southbound/event.py
new/ovsdbapp-2.5.0/ovsdbapp/tests/functional/schema/ovn_southbound/event.py
--- old/ovsdbapp-2.3.0/ovsdbapp/tests/functional/schema/ovn_southbound/event.py
2023-04-27 17:14:09.000000000 +0200
+++ new/ovsdbapp-2.5.0/ovsdbapp/tests/functional/schema/ovn_southbound/event.py
2023-11-21 10:21:52.000000000 +0100
@@ -28,3 +28,14 @@
def match_fn(self, event, row, old):
raise Exception()
+
+
+class MatchFnConditionsEvent(event.WaitEvent):
+ def __init__(self, *args, timeout=1, **kwargs):
+ super().__init__(*args, timeout=timeout, **kwargs)
+
+ def match_fn(self, event, row, old):
+ # This should only be called if we pass all other conditions
+ # so make sure wait() returns False if we see any other events. This
+ # ensures that adding a match_fn() doesn't skip the conditions checks.
+ return True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/ovsdbapp-2.3.0/ovsdbapp/tests/functional/schema/ovn_southbound/test_impl_idl.py
new/ovsdbapp-2.5.0/ovsdbapp/tests/functional/schema/ovn_southbound/test_impl_idl.py
---
old/ovsdbapp-2.3.0/ovsdbapp/tests/functional/schema/ovn_southbound/test_impl_idl.py
2023-04-27 17:14:09.000000000 +0200
+++
new/ovsdbapp-2.5.0/ovsdbapp/tests/functional/schema/ovn_southbound/test_impl_idl.py
2023-11-21 10:21:52.000000000 +0100
@@ -138,3 +138,19 @@
def test_lsp_unbind_if_exists(self):
pname = utils.get_rand_device_name()
self.api.lsp_unbind(pname, if_exists=True).execute(check_error=True)
+
+ def test_event_with_match_fn_and_conditions(self):
+ cond_event = event.MatchFnConditionsEvent(
+ events=(event.MatchFnConditionsEvent.ROW_UPDATE,),
+ table="SB_Global",
+ conditions=(("external_ids", "=", {"foo": "bar"}),))
+ self.handler.watch_event(cond_event)
+ # Test that we match on condition with an Event that has a match_fn()
+ cmd = self.api.db_set("SB_Global", ".", external_ids={"foo": "bar"})
+ cmd.execute(check_error=True)
+ self.assertTrue(cond_event.wait())
+ cond_event.event.clear()
+ # Test that we don't ignore the condition when match_fn() returns True
+ cmd = self.api.db_set("SB_Global", ".", external_ids={"bar": "bar"})
+ cmd.execute(check_error=True)
+ self.assertFalse(cond_event.wait())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ovsdbapp-2.3.0/ovsdbapp/venv.py
new/ovsdbapp-2.5.0/ovsdbapp/venv.py
--- old/ovsdbapp-2.3.0/ovsdbapp/venv.py 2023-04-27 17:14:09.000000000 +0200
+++ new/ovsdbapp-2.5.0/ovsdbapp/venv.py 2023-11-21 10:21:52.000000000 +0100
@@ -282,10 +282,12 @@
class OvsVtepVenvFixture(OvsOvnVenvFixture):
VTEP_SCHEMA = 'vtep.ovsschema'
- def __init__(self, venv, vtepdir=None, **kwargs):
+ def __init__(self, venv, **kwargs):
+ vtepdir = os.getenv('VTEP_SRCDIR')
if vtepdir and os.path.isdir(vtepdir):
self.PATH_VAR_TEMPLATE += ":{0}".format(vtepdir)
- self.vtepdir = self._share_path(self.OVS_PATHS, vtepdir)
+ self.vtepdir = self._share_path(self.OVS_PATHS, vtepdir,
+ [self.VTEP_SCHEMA])
super().__init__(venv, **kwargs)
def _setUp(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ovsdbapp-2.3.0/ovsdbapp.egg-info/PKG-INFO
new/ovsdbapp-2.5.0/ovsdbapp.egg-info/PKG-INFO
--- old/ovsdbapp-2.3.0/ovsdbapp.egg-info/PKG-INFO 2023-04-27
17:14:39.000000000 +0200
+++ new/ovsdbapp-2.5.0/ovsdbapp.egg-info/PKG-INFO 2023-11-21
10:22:19.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: ovsdbapp
-Version: 2.3.0
+Version: 2.5.0
Summary: A library for creating OVSDB applications
Home-page: https://pypi.org/project/ovsdbapp/
Author: OpenStack
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ovsdbapp-2.3.0/ovsdbapp.egg-info/SOURCES.txt
new/ovsdbapp-2.5.0/ovsdbapp.egg-info/SOURCES.txt
--- old/ovsdbapp-2.3.0/ovsdbapp.egg-info/SOURCES.txt 2023-04-27
17:14:39.000000000 +0200
+++ new/ovsdbapp-2.5.0/ovsdbapp.egg-info/SOURCES.txt 2023-11-21
10:22:19.000000000 +0100
@@ -19,6 +19,8 @@
doc/source/contributor/index.rst
doc/source/install/index.rst
doc/source/user/index.rst
+doc/source/user/overview.rst
+doc/source/user/tutorial.rst
ovsdbapp/CHANGES
ovsdbapp/__init__.py
ovsdbapp/api.py
@@ -129,6 +131,7 @@
releasenotes/notes/provide-lrp-get-method-a33a99a7f86b827e.yaml
releasenotes/notes/provide-lrp-networks-modifying-1af13589064c12c6.yaml
releasenotes/source/2023.1.rst
+releasenotes/source/2023.2.rst
releasenotes/source/conf.py
releasenotes/source/index.rst
releasenotes/source/pike.rst
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ovsdbapp-2.3.0/ovsdbapp.egg-info/pbr.json
new/ovsdbapp-2.5.0/ovsdbapp.egg-info/pbr.json
--- old/ovsdbapp-2.3.0/ovsdbapp.egg-info/pbr.json 2023-04-27
17:14:39.000000000 +0200
+++ new/ovsdbapp-2.5.0/ovsdbapp.egg-info/pbr.json 2023-11-21
10:22:19.000000000 +0100
@@ -1 +1 @@
-{"git_version": "770f77d", "is_release": true}
\ No newline at end of file
+{"git_version": "8e55b0b", "is_release": true}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ovsdbapp-2.3.0/releasenotes/source/2023.2.rst
new/ovsdbapp-2.5.0/releasenotes/source/2023.2.rst
--- old/ovsdbapp-2.3.0/releasenotes/source/2023.2.rst 1970-01-01
01:00:00.000000000 +0100
+++ new/ovsdbapp-2.5.0/releasenotes/source/2023.2.rst 2023-11-21
10:21:52.000000000 +0100
@@ -0,0 +1,6 @@
+===========================
+2023.2 Series Release Notes
+===========================
+
+.. release-notes::
+ :branch: stable/2023.2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ovsdbapp-2.3.0/releasenotes/source/index.rst
new/ovsdbapp-2.5.0/releasenotes/source/index.rst
--- old/ovsdbapp-2.3.0/releasenotes/source/index.rst 2023-04-27
17:14:09.000000000 +0200
+++ new/ovsdbapp-2.5.0/releasenotes/source/index.rst 2023-11-21
10:21:52.000000000 +0100
@@ -6,6 +6,7 @@
:maxdepth: 1
unreleased
+ 2023.2
2023.1
zed
yoga
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ovsdbapp-2.3.0/tox.ini new/ovsdbapp-2.5.0/tox.ini
--- old/ovsdbapp-2.3.0/tox.ini 2023-04-27 17:14:09.000000000 +0200
+++ new/ovsdbapp-2.5.0/tox.ini 2023-11-21 10:21:52.000000000 +0100
@@ -65,7 +65,7 @@
OS_TEST_PATH=./ovsdbapp/tests/functional
OVN_SRCDIR={envdir}/src/ovn
OVS_SRCDIR={envdir}/src/ovn/ovs
- VTEP_SRCDIR={envdir}src/ovn/ovs/vtep
+ VTEP_SRCDIR={envdir}/src/ovn/ovs/vtep
OVN_BRANCH={env:OVN_BRANCH:}
passenv = KEEP_VENV
commands =