On Tue, Sep 14, 2021 at 4:48 PM Numan Siddique <[email protected]> wrote:
>
> On Fri, Sep 3, 2021 at 3:29 PM Frode Nordahl
> <[email protected]> wrote:
> >
> > New module contains the infrastructure for registering plugging
> > classes which may be hosted inside or outside the core OVN
> > repository.  The data structures and functions for interacting
> > with these plugging classes also live there.
> >
> > Extend build system to allow enabling building of built-in plugging
> > providers and linking an externally built plugging provider.
> >
> > Signed-off-by: Frode Nordahl <[email protected]>
>
> Hi Frode,
>
> Looks like this patch is having some compilation issues.  Please take
> a look at - https://github.com/ovsrobot/ovn/runs/3509456438

Ok.  You already know about it.  Please ignore this :)

Numan

>
> Thanks
> Numan
>
> > ---
> >  Documentation/automake.mk                     |   1 +
> >  Documentation/topics/index.rst                |   1 +
> >  Documentation/topics/plug_providers/index.rst |  32 +++
> >  .../topics/plug_providers/plug-providers.rst  | 199 ++++++++++++++
> >  acinclude.m4                                  |  49 ++++
> >  configure.ac                                  |   2 +
> >  lib/automake.mk                               |  12 +-
> >  lib/plug-dummy.c                              | 123 +++++++++
> >  lib/plug-dummy.h                              |  33 +++
> >  lib/plug-provider.h                           |  98 +++++++
> >  lib/plug.c                                    | 255 ++++++++++++++++++
> >  lib/plug.h                                    | 107 ++++++++
> >  lib/test-plug.c                               |  72 +++++
> >  ovn-architecture.7.xml                        |  35 ++-
> >  tests/automake.mk                             |   4 +-
> >  tests/ovn-plug.at                             |   8 +
> >  16 files changed, 1016 insertions(+), 15 deletions(-)
> >  create mode 100644 Documentation/topics/plug_providers/index.rst
> >  create mode 100644 Documentation/topics/plug_providers/plug-providers.rst
> >  create mode 100644 lib/plug-dummy.c
> >  create mode 100644 lib/plug-dummy.h
> >  create mode 100644 lib/plug-provider.h
> >  create mode 100644 lib/plug.c
> >  create mode 100644 lib/plug.h
> >  create mode 100644 lib/test-plug.c
> >  create mode 100644 tests/ovn-plug.at
> >
> > diff --git a/Documentation/automake.mk b/Documentation/automake.mk
> > index b3fd3d62b..92a843d76 100644
> > --- a/Documentation/automake.mk
> > +++ b/Documentation/automake.mk
> > @@ -28,6 +28,7 @@ DOC_SOURCE = \
> >         Documentation/topics/ovn-news-2.8.rst \
> >         Documentation/topics/role-based-access-control.rst \
> >         Documentation/topics/debugging-ddlog.rst \
> > +       Documentation/topics/plug_providers/plug-providers.rst \
> >         Documentation/howto/index.rst \
> >         Documentation/howto/docker.rst \
> >         Documentation/howto/firewalld.rst \
> > diff --git a/Documentation/topics/index.rst b/Documentation/topics/index.rst
> > index d58d5618b..12bd113b7 100644
> > --- a/Documentation/topics/index.rst
> > +++ b/Documentation/topics/index.rst
> > @@ -41,6 +41,7 @@ OVN
> >     high-availability
> >     role-based-access-control
> >     ovn-news-2.8
> > +   plug_providers/index
> >     testing
> >
> >  .. list-table::
> > diff --git a/Documentation/topics/plug_providers/index.rst 
> > b/Documentation/topics/plug_providers/index.rst
> > new file mode 100644
> > index 000000000..837eeae15
> > --- /dev/null
> > +++ b/Documentation/topics/plug_providers/index.rst
> > @@ -0,0 +1,32 @@
> > +..
> > +      Licensed under the Apache License, Version 2.0 (the "License"); you 
> > may
> > +      not use this file except in compliance with the License. You may 
> > obtain
> > +      a copy of the License at
> > +
> > +          http://www.apache.org/licenses/LICENSE-2.0
> > +
> > +      Unless required by applicable law or agreed to in writing, software
> > +      distributed under the License is distributed on an "AS IS" BASIS, 
> > WITHOUT
> > +      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See 
> > the
> > +      License for the specific language governing permissions and 
> > limitations
> > +      under the License.
> > +
> > +      Convention for heading levels in OVN documentation:
> > +
> > +      =======  Heading 0 (reserved for the title in a document)
> > +      -------  Heading 1
> > +      ~~~~~~~  Heading 2
> > +      +++++++  Heading 3
> > +      '''''''  Heading 4
> > +
> > +      Avoid deeper levels because they do not render well.
> > +
> > +==============
> > +Plug Providers
> > +==============
> > +
> > +
> > +.. toctree::
> > +   :maxdepth: 2
> > +
> > +   plug-providers
> > diff --git a/Documentation/topics/plug_providers/plug-providers.rst 
> > b/Documentation/topics/plug_providers/plug-providers.rst
> > new file mode 100644
> > index 000000000..7b891156c
> > --- /dev/null
> > +++ b/Documentation/topics/plug_providers/plug-providers.rst
> > @@ -0,0 +1,199 @@
> > +..
> > +      Licensed under the Apache License, Version 2.0 (the "License"); you 
> > may
> > +      not use this file except in compliance with the License. You may 
> > obtain
> > +      a copy of the License at
> > +
> > +          http://www.apache.org/licenses/LICENSE-2.0
> > +
> > +      Unless required by applicable law or agreed to in writing, software
> > +      distributed under the License is distributed on an "AS IS" BASIS, 
> > WITHOUT
> > +      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See 
> > the
> > +      License for the specific language governing permissions and 
> > limitations
> > +      under the License.
> > +
> > +      Convention for heading levels in OVN documentation:
> > +
> > +      =======  Heading 0 (reserved for the title in a document)
> > +      -------  Heading 1
> > +      ~~~~~~~  Heading 2
> > +      +++++++  Heading 3
> > +      '''''''  Heading 4
> > +
> > +      Avoid deeper levels because they do not render well.
> > +
> > +==================
> > +Plugging Providers
> > +==================
> > +
> > +Traditionally it has been the CMSes responsibility to create VIFs as part 
> > of
> > +instance life cycle, and subsequently manage plug/unplug operations on the
> > +integration bridge following the conventions described in the
> > +`Open vSwitch Integration Guide`_ for mapping of VIFs to OVN logical port.
> > +
> > +With the advent of NICs connected to multiple distinct CPUs we can have a
> > +topology where the instance runs on one host and Open vSwitch and OVN runs 
> > on
> > +a different host, the smartnic control plane CPU.  The host facing 
> > interfaces
> > +will be visible to Open vSwitch and OVN as representor ports.
> > +
> > +The actions necessary for plugging and unplugging the representor port in
> > +Open vSwitch running on the smartnic control plane CPU would be the same 
> > for
> > +every CMS.
> > +
> > +Instead of every CMS having to develop their own version of an agent to do
> > +the plugging, we provide a pluggable infrastructure in OVN that allows the
> > +`ovn-controller` to perform the plugging on CMS direction.
> > +
> > +Hardware or platform specific details for initialization and lookup of
> > +representor ports is provided by an plugging provider library hosted 
> > inside or
> > +outside the core OVN repository, and linked at OVN build time.
> > +
> > +Life Cycle of an OVN plugged VIF
> > +--------------------------------
> > +
> > +1. CMS creates a record in the OVN Northbound Logical_Switch_Port table 
> > with
> > +   the options column containing the `plug-type` key with a value 
> > corresponding
> > +   to the `const char *type` provided by the plugging provider 
> > implementation
> > +   as well as a `requested-chassis` key with a value pointing at the name 
> > or
> > +   hostname of the chassis it wants the VIF plugged on.  Additional 
> > plugging
> > +   provider specific key/value pairs must be provided for successful 
> > lookup.
> > +
> > +2. `ovn-northd` looks up the name or hostname provided in the
> > +   `requested-chassis` option and fills the OVN Southbound Port_Binding
> > +   requested_chassis column, it also copies relevant options over to the
> > +   Port_Binding record.
> > +
> > +3. `ovn-controller` monitors Southbound Port_Binding entries wth a
> > +   requested_chassis column pointing at its chassis UUID and when it 
> > encounters
> > +   a entry with option `plug-type` and it has registered a plug provider
> > +   matching that type it will act on it even if no local binding exists 
> > yet.
> > +
> > +4. It will fill the `struct plug_port_ctx_in` as defined in `lib/plug.h` 
> > with
> > +   `op_type` set to 'PLUG_OP_CREATE' and make a call to the plug providers
> > +   `plug_port_prepare` callback function.  Plug provider performs lookup 
> > and
> > +   fills the `struct plug_port_ctx_out` as defined in `lib/plug.h`.
> > +
> > +5. `ovn-controller` creates a port and interface record in the local OVSDB
> > +   using the details provided by the plug provider and also adds
> > +   `external-ids:iface-id` with value matching the logical port name and
> > +   `external-ids:ovn-plugged` with value matching the logical port 
> > `plug-type`.
> > +   When the port creation is done a call will first be made to the plug
> > +   providers `plug_port_finish` function and then to the
> > +   `plug_port_ctx_destroy` function to free any memory allocated by the 
> > plug
> > +   implementation.
> > +
> > +6. The Open vSwitch vswitchd will assign a ofport to the newly created
> > +   interface and on the next `ovn-controller` main loop iteration flows 
> > will be
> > +   installed.
> > +
> > +7. On any change to the Southbound Port_Binding record or full 
> > recomputation
> > +   the `ovn-controller` will in addition to normal flow processing make a 
> > call
> > +   to the plug provider again similar to the first creation in case 
> > anything
> > +   needs updating for the interface record.
> > +
> > +8. The port will be unplugged when an event occurs which would make the
> > +   `ovn-controller` release a logical port, for example the 
> > Logical_Switch_Port
> > +   and Port_Binding entry disappearing from the database or its
> > +   `requested_chassis` column being pointed to a different chassis.
> > +
> > +
> > +The plug provider interface
> > +---------------------------
> > +
> > +The interface between internals of OVN and a plugging provider is a set of
> > +callbacks as defined by the `struct plug_class` in `lib/plug-provider.h`.
> > +
> > +It is important to note that these callbacks will be called in the critical
> > +path of the `ovn-controller` processing loop, so care must be taken to 
> > make the
> > +implementation as efficient as possible, and under no circumstance can any 
> > of
> > +the callback functions make calls that block.
> > +
> > +On `ovn-controller` startup, plug providers made available at build time 
> > will
> > +be registered by the identifier provided in the `const char *type` 
> > pointer, at
> > +this time the `init` function pointer will be called if it is non-NULL.
> > +
> > +> **Note**: apart from the `const char *type` pointer, no attempt will be 
> > made
> > +to access plug provider data or functions before the call to the `init` has
> > +been made.
> > +
> > +On `ovn-controller` exit, the plug providers registered in the above 
> > mentioned
> > +procedure will have their `destroy` function pointer called if it is 
> > non-NULL.
> > +
> > +If the plug provider has internal lookup tables that need to be maintained 
> > they
> > +can define a `run` function which will be called as part of the
> > +`ovn-controller` incremental processing engine loop.  If the changes
> > +encountered can be safely processed incrementally the function should 
> > return
> > +'true', if the changes cannot be processed incrementally, for example 
> > because
> > +the change would impact already handled interfaces that we might not 
> > process in
> > +a while, the function should return 'false' to force the `ovn-controller` 
> > to
> > +perform a full recomputation.
> > +
> > +On update of Interface records the `ovn-controller` will pass on a `sset`
> > +to the `ovsport_update_iface` function containing options the plug
> > +implementation finds pertinent to maintain for successful operation.  This
> > +`sset` is retrieved by making a call to the plug implementation
> > +`plug_get_maintained_iface_options` function pointer if it is non-NULL.  
> > This
> > +allows presence of other users of the OVSDB maintaining a different set of
> > +options on the same set of Interface records without wiping out their 
> > changes.
> > +
> > +Before creating or updating an existing interface record the plug provider
> > +`plug_port_prepare` function pointer will be called with valid pointers to
> > +`struct plug_port_ctx_in` and `struct plug_port_ctx_out` data structures.  
> > If
> > +the plug provider implementation is able to perform lookup it should fill 
> > the
> > +`struct plug_port_ctx_out` data structure and return 'true'.  The
> > +`ovn-controller` will then create or update  the port/interface records and
> > +then call `plug_port_finish` and `plug_port_ctx_destroy`.  If the plug 
> > provider
> > +implementation is unable to perform lookup or prepare the desired resource 
> > it
> > +should return 'false' which will tell the `ovn-controller` to not create or
> > +update the port, in this case it will also not call `plug_port_finish`, it 
> > will
> > +however make a call to `plug_port_ctx_destroy`.
> > +
> > +Before removing port and interface records previously plugged by the
> > +`ovn-controller` as identified by presence of the Interface
> > +`external-ids:ovn-plugged` key, the `ovn-controller` will look up the
> > +`plug-type` from `external-ids:ovn-plugged`, fill `struct plug_port_ctx_in`
> > +with `op_type` set to 'PLUG_OP_REMOVE' and make a call to 
> > `plug_port_prepare`.
> > +After the port and interface has been removed a call will be made to
> > +`plug_port_finish`.  Both calls will be made with the pointer to
> > +`plug_port_ctx_out` set to 'NULL', and no call will be made to
> > +`plug_port_ctx_destroy`.
> > +
> > +Building with in-tree plugging providers
> > +----------------------------------------
> > +
> > +Plugging providers hosted in the OVN repository living under
> > +`lib/plug_providers`:
> > +
> > +To enable them, provide the `--enable-plug-providers` command line option 
> > to
> > +the configure script when building OVN.
> > +
> > +Building with an externally provided plugging provider
> > +------------------------------------------------------
> > +
> > +There is also infrastructure in place to support linking OVN with an 
> > externally
> > +built plugging provider.
> > +
> > +This external plugging provider must define a NULL-terminated array of 
> > pointers
> > +to `struct plug_class` data structures named `plug_provider_classes`.  
> > Example:
> > +
> > +.. code-block:: C
> > +
> > +   const struct plug_class *plug_provider_classes[] = {
> > +       &plug_foo,
> > +       NULL,
> > +   };
> > +
> > +The name of the repostiroy for the external plugging provider should be the
> > +same as the name of the library it produces, and the built library artifact
> > +should be placed in lib/.libs.  Example:
> > +
> > +.. code-block:: none
> > +
> > +   ovn-vif-foo/
> > +   ovn-vif-foo/lib/.libs/libovn-vif-foo.la
> > +
> > +To enable such a plugging provider provide the
> > +`--with-plug-provider=/path/to/ovn-vif-foo` command line option to the
> > +configure script when building OVN.
> > +
> > +.. LINKS
> > +.. _Open vSwitch Integration Guide: 
> > https://docs.openvswitch.org/en/latest/topics/integration/
> > diff --git a/acinclude.m4 b/acinclude.m4
> > index e7f829520..793a073d1 100644
> > --- a/acinclude.m4
> > +++ b/acinclude.m4
> > @@ -441,3 +441,52 @@ AC_DEFUN([OVN_CHECK_OVS], [
> >    AC_MSG_CHECKING([OVS version])
> >    AC_MSG_RESULT([$OVSVERSION])
> >  ])
> > +
> > +dnl OVN_CHECK_PLUG_PROVIDER
> > +dnl
> > +dnl Check for external plug provider
> > +AC_DEFUN([OVN_CHECK_PLUG_PROVIDER], [
> > +  AC_ARG_VAR([PLUG_PROVIDER])
> > +  AC_ARG_WITH(
> > +    [plug-provider],
> > +    [AC_HELP_STRING([--with-plug-provider=/path/to/provider/repository],
> > +                    [Specify path to a configured and built plug provider 
> > repository])],
> > +    [if test "$withval" = yes; then
> > +       if test -z "$PLUG_PROVIDER"; then
> > +         AC_MSG_ERROR([To build with external plug provider, specify the 
> > path to a configured and built plug provider repository 
> > --with-plug-provider or in \$PLUG_PROVIDER]),
> > +       fi
> > +       PLUG_PROVIDER="$(realpath $PLUG_PROVIDER)"
> > +     else
> > +       PLUG_PROVIDER="$(realpath $withval)"
> > +     fi
> > +     _plug_provider_name="$(basename $PLUG_PROVIDER)"
> > +     if test ! -f "$PLUG_PROVIDER/lib/.libs/lib${_plug_provider_name}.la"; 
> > then
> > +       AC_MSG_ERROR([$withval is not a configured and built plug provider 
> > library repository])
> > +     fi
> > +     PLUG_PROVIDER_LDFLAGS="-L$PLUG_PROVIDER/lib/.libs 
> > -l$_plug_provider_name"
> > +    ],
> > +    [PLUG_PROVIDER=no])
> > +  AC_MSG_CHECKING([for plug provider])
> > +  AC_MSG_RESULT([$PLUG_PROVIDER])
> > +  AC_SUBST([PLUG_PROVIDER_LDFLAGS])
> > +  AM_CONDITIONAL([HAVE_PLUG_PROVIDER], [test "$PLUG_PROVIDER" != no])
> > +  if test "$PLUG_PROVIDER" != no; then
> > +    AC_DEFINE([HAVE_PLUG_PROVIDER], [1],
> > +              [Build and link with external plug provider])
> > +  fi
> > +])
> > +
> > +dnl OVN_ENABLE_PLUG
> > +dnl
> > +dnl Enable built-in plug providers
> > +AC_DEFUN([OVN_ENABLE_PLUG], [
> > +    AC_ARG_ENABLE(
> > +      [plug-providers],
> > +      [AC_HELP_STRING([--enable-plug-providers], [Enable building of 
> > built-in plug providers])],
> > +      [], [enable_plug=no])
> > +    AM_CONDITIONAL([ENABLE_PLUG], [test "$enable_plug" != no])
> > +    if test "$enable_plug" != no; then
> > +      AC_DEFINE([ENABLE_PLUG], [1],
> > +                [Build built-in plug providers])
> > +    fi
> > +])
> > diff --git a/configure.ac b/configure.ac
> > index df0b98295..7f3274e59 100644
> > --- a/configure.ac
> > +++ b/configure.ac
> > @@ -172,6 +172,8 @@ OVS_ENABLE_SPARSE
> >  OVS_CHECK_DDLOG([0.38])
> >  OVS_CHECK_PRAGMA_MESSAGE
> >  OVN_CHECK_OVS
> > +OVN_CHECK_PLUG_PROVIDER
> > +OVN_ENABLE_PLUG
> >  OVS_CTAGS_IDENTIFIERS
> >  AC_SUBST([OVS_CFLAGS])
> >  AC_SUBST([OVS_LDFLAGS])
> > diff --git a/lib/automake.mk b/lib/automake.mk
> > index ddfe33948..086fbd62d 100644
> > --- a/lib/automake.mk
> > +++ b/lib/automake.mk
> > @@ -3,6 +3,11 @@ lib_libovn_la_LDFLAGS = \
> >          $(OVS_LTINFO) \
> >          -Wl,--version-script=$(top_builddir)/lib/libovn.sym \
> >          $(AM_LDFLAGS)
> > +
> > +if HAVE_PLUG_PROVIDER
> > +lib_libovn_la_LDFLAGS += $(PLUG_PROVIDER_LDFLAGS)
> > +endif
> > +
> >  lib_libovn_la_SOURCES = \
> >         lib/acl-log.c \
> >         lib/acl-log.h \
> > @@ -32,7 +37,12 @@ lib_libovn_la_SOURCES = \
> >         lib/inc-proc-eng.h \
> >         lib/lb.c \
> >         lib/lb.h \
> > -       lib/stopwatch-names.h
> > +       lib/stopwatch-names.h \
> > +       lib/plug-provider.h \
> > +       lib/plug.h \
> > +       lib/plug.c \
> > +       lib/plug-dummy.h \
> > +       lib/plug-dummy.c
> >  nodist_lib_libovn_la_SOURCES = \
> >         lib/ovn-dirs.c \
> >         lib/ovn-nb-idl.c \
> > diff --git a/lib/plug-dummy.c b/lib/plug-dummy.c
> > new file mode 100644
> > index 000000000..505c37c9c
> > --- /dev/null
> > +++ b/lib/plug-dummy.c
> > @@ -0,0 +1,123 @@
> > +/*
> > + * Copyright (c) 2021 Canonical
> > + *
> > + * Licensed under the Apache License, Version 2.0 (the "License");
> > + * you may not use this file except in compliance with the License.
> > + * You may obtain a copy of the License at:
> > + *
> > + *     http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing, software
> > + * distributed under the License is distributed on an "AS IS" BASIS,
> > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> > + * See the License for the specific language governing permissions and
> > + * limitations under the License.
> > + */
> > +
> > +#include <config.h>
> > +#include "plug-dummy.h"
> > +#include "plug-provider.h"
> > +#include "plug.h"
> > +
> > +#include <stdint.h>
> > +
> > +#include "openvswitch/vlog.h"
> > +#include "smap.h"
> > +#include "sset.h"
> > +
> > +#ifndef IFNAMSIZ
> > +#define IFNAMSIZ 16
> > +#endif
> > +
> > +VLOG_DEFINE_THIS_MODULE(plug_dummy);
> > +
> > +static struct sset plug_dummy_maintained_iface_options;
> > +
> > +static int
> > +plug_dummy_init(void)
> > +{
> > +    sset_init(&plug_dummy_maintained_iface_options);
> > +    sset_add(&plug_dummy_maintained_iface_options, "plug-dummy-option");
> > +
> > +    return 0;
> > +}
> > +
> > +static int
> > +plug_dummy_destroy(void)
> > +{
> > +    sset_destroy(&plug_dummy_maintained_iface_options);
> > +
> > +    return 0;
> > +}
> > +
> > +static const struct sset*
> > +plug_dummy_get_maintained_iface_options(void)
> > +{
> > +    return &plug_dummy_maintained_iface_options;
> > +}
> > +
> > +static bool
> > +plug_dummy_run(struct plug_class *plug)
> > +{
> > +    VLOG_DBG("plug_dummy_run(%p)", plug);
> > +
> > +    return true;
> > +}
> > +
> > +static bool
> > +plug_dummy_port_prepare(const struct plug_port_ctx_in *ctx_in,
> > +                       struct plug_port_ctx_out *ctx_out)
> > +{
> > +    VLOG_DBG("plug_dummy_port_prepare: %s", ctx_in->lport_name);
> > +
> > +    if (ctx_in->op_type == PLUG_OP_CREATE) {
> > +        size_t lport_name_len = strlen(ctx_in->lport_name);
> > +        ctx_out->name = xzalloc(IFNAMSIZ);
> > +        memcpy(ctx_out->name, ctx_in->lport_name,
> > +               (lport_name_len < IFNAMSIZ) ? lport_name_len : IFNAMSIZ - 
> > 1);
> > +        ctx_out->type = xstrdup("internal");
> > +        ctx_out->iface_options = xmalloc(sizeof *ctx_out->iface_options);
> > +        smap_init(ctx_out->iface_options);
> > +        smap_add(ctx_out->iface_options, "plug-dummy-option", "value");
> > +    }
> > +
> > +    return true;
> > +}
> > +
> > +static void
> > +plug_dummy_port_finish(const struct plug_port_ctx_in *ctx_in,
> > +                      struct plug_port_ctx_out *ctx_out OVS_UNUSED)
> > +{
> > +    VLOG_DBG("plug_dummy_port_finish: %s", ctx_in->lport_name);
> > +}
> > +
> > +static void
> > +plug_dummy_port_ctx_destroy(const struct plug_port_ctx_in *ctx_in,
> > +                           struct plug_port_ctx_out *ctx_out)
> > +{
> > +    VLOG_DBG("plug_dummy_port_ctx_destroy: %s", ctx_in->lport_name);
> > +    ovs_assert(ctx_in->op_type == PLUG_OP_CREATE);
> > +    free(ctx_out->name);
> > +    free(ctx_out->type);
> > +    smap_destroy(ctx_out->iface_options);
> > +    free(ctx_out->iface_options);
> > +}
> > +
> > +const struct plug_class plug_dummy_class = {
> > +    .type = "dummy",
> > +    .init = plug_dummy_init,
> > +    .destroy = plug_dummy_destroy,
> > +    .plug_get_maintained_iface_options =
> > +        plug_dummy_get_maintained_iface_options,
> > +    .run = plug_dummy_run,
> > +    .plug_port_prepare = plug_dummy_port_prepare,
> > +    .plug_port_finish = plug_dummy_port_finish,
> > +    .plug_port_ctx_destroy = plug_dummy_port_ctx_destroy,
> > +};
> > +
> > +void
> > +plug_dummy_enable(void)
> > +{
> > +    plug_register_provider(&plug_dummy_class);
> > +}
> > +
> > diff --git a/lib/plug-dummy.h b/lib/plug-dummy.h
> > new file mode 100644
> > index 000000000..6ea33671e
> > --- /dev/null
> > +++ b/lib/plug-dummy.h
> > @@ -0,0 +1,33 @@
> > +/*
> > + * Copyright (c) 2021 Canonical
> > + *
> > + * Licensed under the Apache License, Version 2.0 (the "License");
> > + * you may not use this file except in compliance with the License.
> > + * You may obtain a copy of the License at:
> > + *
> > + *     http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing, software
> > + * distributed under the License is distributed on an "AS IS" BASIS,
> > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> > + * See the License for the specific language governing permissions and
> > + * limitations under the License.
> > + */
> > +
> > +#ifndef PLUG_DUMMY_H
> > +#define PLUG_DUMMY_H 1
> > +
> > +/*
> > + * The dummy plugger, allows for experimenting with plugging in a sandbox 
> > */
> > +
> > +#ifdef  __cplusplus
> > +extern "C" {
> > +#endif
> > +
> > +void plug_dummy_enable(void);
> > +
> > +#ifdef  __cplusplus
> > +}
> > +#endif
> > +
> > +#endif /* plug-dummy.h */
> > diff --git a/lib/plug-provider.h b/lib/plug-provider.h
> > new file mode 100644
> > index 000000000..487534ee5
> > --- /dev/null
> > +++ b/lib/plug-provider.h
> > @@ -0,0 +1,98 @@
> > +/*
> > + * Copyright (c) 2021 Canonical
> > + *
> > + * Licensed under the Apache License, Version 2.0 (the "License");
> > + * you may not use this file except in compliance with the License.
> > + * You may obtain a copy of the License at:
> > + *
> > + *     http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing, software
> > + * distributed under the License is distributed on an "AS IS" BASIS,
> > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> > + * See the License for the specific language governing permissions and
> > + * limitations under the License.
> > + */
> > +
> > +#ifndef PLUG_PROVIDER_H
> > +#define PLUG_PROVIDER_H 1
> > +
> > +/* Provider interface to pluggers.  A plugger implementation performs 
> > lookup
> > + * and/or initialization of ports, typically representor ports, using 
> > generic
> > + * non-blocking hardware interfaces.  This allows the ovn-controller to, 
> > upon
> > + * the CMS's request, create ports and interfaces in the chassis's Open 
> > vSwitch
> > + * instances (also known as vif plugging).
> > + */
> > +
> > +#include <stdbool.h>
> > +
> > +#include "plug.h"
> > +
> > +#ifdef __cplusplus
> > +extern "C" {
> > +#endif
> > +
> > +struct plug_class {
> > +    /* Type of plugger in this class. */
> > +    const char *type;
> > +
> > +    /* Called when the plug provider is registered, typically at program
> > +     * startup.
> > +     *
> > +     * This function may be set to null if a plug class needs no
> > +     * initialization at registration time. */
> > +    int (*init)(void);
> > +
> > +    /* Called when the plug provider is unregistered, typically at program
> > +     * exit.
> > +     *
> > +     * This function may be set to null if a plug class needs no
> > +     * de-initialization at unregister time.*/
> > +    int (*destroy)(void);
> > +
> > +    /* Performs periodic work needed by plugger, if any is necessary.  
> > Returns
> > +     * 'true; if the changes encountered could be handled incrementally,
> > +     * 'false' otherwise.
> > +     *
> > +     * Note that returning 'false' will instruct the incremental processing
> > +     * engine to perform a full recomputation. */
> > +    bool (*run)(struct plug_class *);
> > +
> > +    /* Retrieve Interface options this plugger will maintain.  This set is 
> > used
> > +     * to know which items to remove when maintaining the database record. 
> > */
> > +    const struct sset * (*plug_get_maintained_iface_options)(void);
> > +
> > +    /* Pass plug_port_ctx_in to plug implementation to prepare for port
> > +     * creation/update.
> > +     *
> > +     * The plug implemantation can perform lookup or any per port
> > +     * initialization and should fill plug_port_ctx_out with data required 
> > for
> > +     * port/interface creation.  The plug implementation should return 
> > true if
> > +     * it wants the caller to create/update a port/interface, false 
> > otherwise.
> > +     *
> > +     * Data in the plug_port_ctx_out struct is owned by the plugging 
> > library,
> > +     * and a call must be made to the plug_port_ctx_destroy callback to 
> > free
> > +     * up any allocations when done with port creation/update.
> > +     */
> > +    bool (*plug_port_prepare)(const struct plug_port_ctx_in *,
> > +                              struct plug_port_ctx_out *);
> > +
> > +    /* Notify plugging library that port update is done. */
> > +    void (*plug_port_finish)(const struct plug_port_ctx_in *,
> > +                             struct plug_port_ctx_out *);
> > +
> > +    /* Free any allocations made by the plug_port_prepare callback. */
> > +    void (*plug_port_ctx_destroy)(const struct plug_port_ctx_in *,
> > +                                  struct plug_port_ctx_out *);
> > +};
> > +
> > +extern const struct plug_class plug_dummy_class;
> > +#ifdef HAVE_PLUG_PROVIDER
> > +extern const struct plug_class *plug_provider_classes[];
> > +#endif
> > +
> > +#ifdef  __cplusplus
> > +}
> > +#endif
> > +
> > +#endif /* plug-provider.h */
> > diff --git a/lib/plug.c b/lib/plug.c
> > new file mode 100644
> > index 000000000..c0c34214e
> > --- /dev/null
> > +++ b/lib/plug.c
> > @@ -0,0 +1,255 @@
> > +/*
> > + * Copyright (c) 2021 Canonical
> > + *
> > + * Licensed under the Apache License, Version 2.0 (the "License");
> > + * you may not use this file except in compliance with the License.
> > + * You may obtain a copy of the License at:
> > + *
> > + *     http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing, software
> > + * distributed under the License is distributed on an "AS IS" BASIS,
> > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> > + * See the License for the specific language governing permissions and
> > + * limitations under the License.
> > + */
> > +
> > +#include <config.h>
> > +#include "plug-provider.h"
> > +#include "plug.h"
> > +
> > +#include <errno.h>
> > +#include <stdint.h>
> > +#include <string.h>
> > +
> > +#include "openvswitch/vlog.h"
> > +#include "openvswitch/shash.h"
> > +#include "smap.h"
> > +#include "sset.h"
> > +#include "lib/inc-proc-eng.h"
> > +
> > +VLOG_DEFINE_THIS_MODULE(plug);
> > +
> > +#ifdef ENABLE_PLUG
> > +static const struct plug_class *base_plug_classes[] = {
> > +};
> > +#endif
> > +
> > +static struct shash plug_classes = SHASH_INITIALIZER(&plug_classes);
> > +
> > +/* Protects the 'plug_classes' shash. */
> > +static struct ovs_mutex plug_classes_mutex = OVS_MUTEX_INITIALIZER;
> > +
> > +/* Initialize the the plug infrastructure by registering known plug 
> > classes */
> > +void
> > +plug_initialize(void)
> > +{
> > +    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
> > +
> > +    if (ovsthread_once_start(&once)) {
> > +#ifdef ENABLE_PLUG
> > +        /* Register built-in plug provider classes */
> > +        for (int i = 0; i < ARRAY_SIZE(base_plug_classes); i++) {
> > +            plug_register_provider(base_plug_classes[i]);
> > +        }
> > +#endif
> > +#ifdef HAVE_PLUG_PROVIDER
> > +        /* Register external plug provider classes.
> > +         *
> > +         * Note that we cannot use the ARRAY_SIZE macro here as
> > +         * plug_provider_classes is defined in external code which is not
> > +         * available at compile time.  The convention is to use a
> > +         * NULL-terminated array instead. */
> > +        for (const struct plug_class **pp = plug_provider_classes;
> > +             pp && *pp;
> > +             pp++)
> > +        {
> > +            plug_register_provider(*pp);
> > +        }
> > +#endif
> > +        ovsthread_once_done(&once);
> > +    }
> > +}
> > +
> > +static int
> > +plug_register_provider__(const struct plug_class *new_class)
> > +{
> > +    struct plug_class *plug_class;
> > +    int error;
> > +
> > +    if (shash_find(&plug_classes, new_class->type)) {
> > +        VLOG_WARN("attempted to register duplicate plug provider: %s",
> > +                  new_class->type);
> > +        return EEXIST;
> > +    }
> > +
> > +    error = new_class->init ? new_class->init() : 0;
> > +    if (error) {
> > +        VLOG_WARN("failed to initialize %s plug class: %s",
> > +                  new_class->type, ovs_strerror(error));
> > +        return error;
> > +    }
> > +
> > +    plug_class = xmalloc(sizeof *plug_class);
> > +    memcpy(plug_class, new_class, sizeof *plug_class);
> > +
> > +    shash_add(&plug_classes, new_class->type, plug_class);
> > +
> > +    return 0;
> > +}
> > +
> > +/* Register the new plug provider referred to in 'new_class' and perform 
> > any
> > + * class level initialization as specified in its plug_class. */
> > +int
> > +plug_register_provider(const struct plug_class *new_class)
> > +{
> > +    int error;
> > +
> > +    ovs_mutex_lock(&plug_classes_mutex);
> > +    error = plug_register_provider__(new_class);
> > +    ovs_mutex_unlock(&plug_classes_mutex);
> > +
> > +    return error;
> > +}
> > +
> > +static int
> > +plug_unregister_provider__(const char *type)
> > +{
> > +    int error;
> > +    struct shash_node *node;
> > +    struct plug_class *plug_class;
> > +
> > +    node = shash_find(&plug_classes, type);
> > +    if (!node) {
> > +        return EINVAL;
> > +    }
> > +
> > +    plug_class = node->data;
> > +    error = plug_class->destroy ? plug_class->destroy() : 0;
> > +    if (error) {
> > +        VLOG_WARN("failed to destroy %s plug class: %s",
> > +                  plug_class->type, ovs_strerror(error));
> > +        return error;
> > +    }
> > +
> > +    shash_delete(&plug_classes, node);
> > +    free(plug_class);
> > +
> > +    return 0;
> > +}
> > +
> > +/* Unregister the plug provider identified by 'type' and perform any class
> > + * level de-initialization as specified in its plug_class. */
> > +int
> > +plug_unregister_provider(const char *type)
> > +{
> > +    int error;
> > +
> > +    ovs_mutex_lock(&plug_classes_mutex);
> > +    error = plug_unregister_provider__(type);
> > +    ovs_mutex_unlock(&plug_classes_mutex);
> > +
> > +    return error;
> > +}
> > +
> > +const struct plug_class *
> > +plug_get_provider(const char *type)
> > +{
> > +    struct plug_class *plug_class;
> > +
> > +    ovs_mutex_lock(&plug_classes_mutex);
> > +    plug_class = shash_find_data(&plug_classes, type);
> > +    ovs_mutex_unlock(&plug_classes_mutex);
> > +
> > +    return plug_class;
> > +}
> > +
> > +/* De-initialize and unregister the plug provider classes. */
> > +void
> > +plug_destroy_all(void)
> > +{
> > +    struct shash_node *node, *next;
> > +
> > +    SHASH_FOR_EACH_SAFE (node, next, &plug_classes) {
> > +        struct plug_class *plug_class = node->data;
> > +        plug_unregister_provider(plug_class->type);
> > +    }
> > +}
> > +
> > +/* Get the class level 'maintained_iface_options' set. */
> > +const struct sset *
> > +plug_get_maintained_iface_options(const struct plug_class *plug_class)
> > +{
> > +    return plug_class->plug_get_maintained_iface_options();
> > +}
> > +
> > +/* Prepare the logical port as identified by 'ctx_in' for port creation, 
> > update
> > + * or removal as specified by 'ctx_in->op_type'.
> > + *
> > + * When 'ctx_in->op_type' is PLUG_OP_CREATE the plug implementation must 
> > fill
> > + * 'ctx_out' with data to apply to the interface record maintained by OVN 
> > on
> > + * its behalf.
> > + *
> > + * When 'ctx_in_op_type' is PLUG_OP_REMOVE 'ctx_out' should be set to NULL 
> > and
> > + * the plug implementation must not attempt to use 'ctx_out'.
> > + *
> > + * The data in 'ctx_out' is owned by the plug implementation, and a call 
> > must
> > + * be made to plug_port_ctx_destroy when done with it. */
> > +bool
> > +plug_port_prepare(const struct plug_class *plug_class,
> > +                  const struct plug_port_ctx_in *ctx_in,
> > +                  struct plug_port_ctx_out *ctx_out)
> > +{
> > +    if (ctx_out) {
> > +        memset(ctx_out, 0, sizeof(*ctx_out));
> > +    }
> > +    return plug_class->plug_port_prepare(ctx_in, ctx_out);
> > +}
> > +
> > +/* Notify the plug implementation that a port creation, update or removal 
> > has
> > + * been completed */
> > +void
> > +plug_port_finish(const struct plug_class *plug_class,
> > +                 const struct plug_port_ctx_in *ctx_in,
> > +                 struct plug_port_ctx_out *ctx_out)
> > +{
> > +    plug_class->plug_port_finish(ctx_in, ctx_out);
> > +}
> > +
> > +/* Free any data allocated to 'ctx_out' in a prevous call to
> > + * plug_port_prepare. */
> > +void
> > +plug_port_ctx_destroy(const struct plug_class *plug_class,
> > +                      const struct plug_port_ctx_in *ctx_in,
> > +                      struct plug_port_ctx_out *ctx_out)
> > +{
> > +    plug_class->plug_port_ctx_destroy(ctx_in, ctx_out);
> > +}
> > +
> > +/* Iterate over registered plug provider classes and call their 'run'
> > + * function if defined.
> > + *
> > + * If any of the classes report that something has changed we will trigger 
> > a
> > + * recompute. */
> > +void
> > +en_plug_provider_run(struct engine_node *inc_eng_node,
> > +                     void *inc_eng_data OVS_UNUSED)
> > +{
> > +    struct shash_node *node, *next;
> > +    bool handled = true;
> > +
> > +    SHASH_FOR_EACH_SAFE (node, next, &plug_classes) {
> > +        struct plug_class *plug_class = node->data;
> > +        if (plug_class->run && !plug_class->run(plug_class)) {
> > +            handled = false;
> > +        }
> > +    }
> > +
> > +    if (!handled) {
> > +        /* as we do not have a change handler registered in the incremental
> > +         * processing engine this will trigger a recompute. */
> > +        engine_set_node_state(inc_eng_node, EN_UPDATED);
> > +    } else {
> > +        engine_set_node_state(inc_eng_node, EN_UNCHANGED);
> > +    }
> > +}
> > diff --git a/lib/plug.h b/lib/plug.h
> > new file mode 100644
> > index 000000000..fef4c7f64
> > --- /dev/null
> > +++ b/lib/plug.h
> > @@ -0,0 +1,107 @@
> > +/*
> > + * Copyright (c) 2021 Canonical
> > + *
> > + * Licensed under the Apache License, Version 2.0 (the "License");
> > + * you may not use this file except in compliance with the License.
> > + * You may obtain a copy of the License at:
> > + *
> > + *     http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing, software
> > + * distributed under the License is distributed on an "AS IS" BASIS,
> > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> > + * See the License for the specific language governing permissions and
> > + * limitations under the License.
> > + */
> > +
> > +#ifndef PLUG_H
> > +#define PLUG_H 1
> > +
> > +/*
> > + * Plug, the plugging interface.  This module contains the infrastructure 
> > for
> > + * registering and instantiating plugging classes which may be hosted 
> > inside
> > + * or outside the core OVN repository.  The data structures and functions 
> > for
> > + * interacting with these plugging classes also live here.
> > + */
> > +
> > +#include "smap.h"
> > +
> > +#ifdef  __cplusplus
> > +extern "C" {
> > +#endif
> > +
> > +struct plug_class;
> > +struct ovsdb_idl_txn;
> > +struct ovsrec_bridge;
> > +
> > +enum plug_op_type {
> > +    PLUG_OP_CREATE = 1, /* Port is created or updated */
> > +    PLUG_OP_REMOVE,     /* Port is removed from this chassis */
> > +};
> > +
> > +struct plug_port_ctx_in {
> > +    /* Operation being performed */
> > +    enum plug_op_type op_type;
> > +
> > +    /* These are provided so that the plug implementation may make 
> > decisions
> > +     * based on environmental factors such as settings in the open-vswitch
> > +     * table and datapath type settings on the integration bridge. */
> > +    const struct ovsrec_open_vswitch_table *ovs_table;
> > +    const struct ovsrec_bridge *br_int;
> > +
> > +    /* Name of logical port, can be useful for plugging library to track 
> > any
> > +     * per port resource initialization. */
> > +    const char *lport_name;
> > +
> > +    /* Logical port options, while OVN will forward the contents verbatim 
> > from
> > +     * the Southbound database, the convention is for the plugging library 
> > to
> > +     * only make decisions based on the plug-* options. */
> > +    const struct smap *lport_options;
> > +
> > +    /* When OVN knows about an existing interface record associated with 
> > this
> > +     * lport, these will be filled in with information about it. */
> > +    const char *iface_name;
> > +    const char *iface_type;
> > +    const struct smap *iface_options;
> > +};
> > +
> > +struct plug_port_ctx_out {
> > +    /* The name to use for port and interface record. */
> > +    char *name;
> > +
> > +    /* Type of interface to create. */
> > +    char *type;
> > +
> > +    /* Options to set on the interface record. */
> > +    struct smap *iface_options;
> > +};
> > +
> > +
> > +void plug_initialize(void);
> > +int plug_register_provider(const struct plug_class *);
> > +int plug_unregister_provider(const char *type);
> > +void plug_destroy_all(void);
> > +const struct plug_class * plug_get_provider(const char *);
> > +
> > +const struct sset * plug_get_maintained_iface_options(
> > +    const struct plug_class *plug_class);
> > +
> > +bool plug_port_prepare(const struct plug_class *,
> > +                       const struct plug_port_ctx_in *,
> > +                       struct plug_port_ctx_out *);
> > +void plug_port_finish(const struct plug_class *,
> > +                      const struct plug_port_ctx_in *,
> > +                      struct plug_port_ctx_out *);
> > +void plug_port_ctx_destroy(const struct plug_class *,
> > +                           const struct plug_port_ctx_in *,
> > +                           struct plug_port_ctx_out *);
> > +
> > +struct engine_node;
> > +
> > +void en_plug_provider_run(struct engine_node *, void *);
> > +
> > +#ifdef  __cplusplus
> > +}
> > +#endif
> > +
> > +#endif /* plug.h */
> > diff --git a/lib/test-plug.c b/lib/test-plug.c
> > new file mode 100644
> > index 000000000..bddce23c4
> > --- /dev/null
> > +++ b/lib/test-plug.c
> > @@ -0,0 +1,72 @@
> > +/* Copyright (c) 2021, Canonical
> > + *
> > + * Licensed under the Apache License, Version 2.0 (the "License");
> > + * you may not use this file except in compliance with the License.
> > + * You may obtain a copy of the License at:
> > + *
> > + *     http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing, software
> > + * distributed under the License is distributed on an "AS IS" BASIS,
> > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> > + * See the License for the specific language governing permissions and
> > + * limitations under the License.
> > + */
> > +
> > +#include <config.h>
> > +#include <errno.h>
> > +
> > +#include "plug.h"
> > +#include "plug-dummy.h"
> > +#include "plug-provider.h"
> > +#include "smap.h"
> > +#include "sset.h"
> > +#include "tests/ovstest.h"
> > +
> > +static void
> > +test_plug(struct ovs_cmdl_context *ctx OVS_UNUSED)
> > +{
> > +    const struct plug_class *plug_class;
> > +
> > +    ovs_assert(plug_unregister_provider("dummy") == EINVAL);
> > +
> > +    ovs_assert(!plug_register_provider(&plug_dummy_class));
> > +    plug_class = plug_get_provider("dummy");
> > +    ovs_assert(plug_register_provider(&plug_dummy_class) == EEXIST);
> > +
> > +    ovs_assert(sset_contains(plug_get_maintained_iface_options(plug_class),
> > +                             "plug-dummy-option"));
> > +
> > +    struct smap fake_lport_options = SMAP_INITIALIZER(&fake_lport_options);
> > +    struct plug_port_ctx_in ctx_in = {
> > +        .op_type = PLUG_OP_CREATE,
> > +        .lport_name = "lsp1",
> > +        .lport_options = &fake_lport_options,
> > +    };
> > +    struct plug_port_ctx_out ctx_out;
> > +    plug_port_prepare(plug_class, &ctx_in, &ctx_out);
> > +    ovs_assert(!strcmp(ctx_out.name, "lsp1"));
> > +    ovs_assert(!strcmp(ctx_out.type, "internal"));
> > +    ovs_assert(!strcmp(smap_get(
> > +            ctx_out.iface_options, "plug-dummy-option"), "value"));
> > +
> > +    plug_port_finish(plug_class, &ctx_in, &ctx_out);
> > +    plug_port_ctx_destroy(plug_class, &ctx_in, &ctx_out);
> > +    plug_destroy_all();
> > +}
> > +
> > +static void
> > +test_plug_main(int argc, char *argv[])
> > +{
> > +    set_program_name(argv[0]);
> > +    static const struct ovs_cmdl_command commands[] = {
> > +        {"run", NULL, 0, 0, test_plug, OVS_RO},
> > +        {NULL, NULL, 0, 0, NULL, OVS_RO},
> > +    };
> > +    struct ovs_cmdl_context ctx;
> > +    ctx.argc = argc - 1;
> > +    ctx.argv = argv + 1;
> > +    ovs_cmdl_run_command(&ctx, commands);
> > +}
> > +
> > +OVSTEST_REGISTER("test-plug", test_plug_main);
> > diff --git a/ovn-architecture.7.xml b/ovn-architecture.7.xml
> > index 3d2910358..152367a89 100644
> > --- a/ovn-architecture.7.xml
> > +++ b/ovn-architecture.7.xml
> > @@ -67,8 +67,9 @@
> >      <li>
> >        One or more (usually many) <dfn>hypervisors</dfn>.  Hypervisors must 
> > run
> >        Open vSwitch and implement the interface described in
> > -      <code>Documentation/topics/integration.rst</code> in the OVN source 
> > tree.
> > -      Any hypervisor platform supported by Open vSwitch is acceptable.
> > +      <code>Documentation/topics/integration.rst</code> in the Open vSwitch
> > +      source tree.  Any hypervisor platform supported by Open vSwitch is
> > +      acceptable.
> >      </li>
> >
> >      <li>
> > @@ -318,11 +319,19 @@
> >
> >      <li>
> >        On a hypervisor, any VIFs that are to be attached to logical 
> > networks.
> > -      The hypervisor itself, or the integration between Open vSwitch and 
> > the
> > -      hypervisor (described in
> > -      <code>Documentation/topics/integration.rst</code>) takes care of 
> > this.
> > -      (This is not part of OVN or new to OVN; this is pre-existing 
> > integration
> > -      work that has already been done on hypervisors that support OVS.)
> > +      For instances connected through software emulated ports such as 
> > TUN/TAP
> > +      or VETH pairs, the hypervisor itself will normally create ports and 
> > plug
> > +      them into the integration bridge.  For instances connected through
> > +      representor ports, typically used with hardware offload, the
> > +      <code>ovn-controller</code> may on CMS direction consult a plugging
> > +      provider library for representor port lookup and plug them into the
> > +      integration bridge (please refer to
> > +      <code>Documentation/topics/plugging-providers.rst</code> for more
> > +      information).  In both cases the conventions described in
> > +      <code>Documentation/topics/integration.rst</code> in the Open vSwitch
> > +      source tree is followed to ensure mapping between OVN logical port 
> > and
> > +      VIF.  (This is pre-existing integration work that has already been 
> > done
> > +      on hypervisors that support OVS.)
> >      </li>
> >
> >      <li>
> > @@ -921,12 +930,12 @@
> >        Eventually, a user powers on the VM that owns the VIF.  On the 
> > hypervisor
> >        where the VM is powered on, the integration between the hypervisor 
> > and
> >        Open vSwitch (described in
> > -      <code>Documentation/topics/integration.rst</code>) adds the VIF to 
> > the OVN
> > -      integration bridge and stores <var>vif-id</var> in
> > -      <code>external_ids</code>:<code>iface-id</code> to indicate that the
> > -      interface is an instantiation of the new VIF.  (None of this code is 
> > new
> > -      in OVN; this is pre-existing integration work that has already been 
> > done
> > -      on hypervisors that support OVS.)
> > +      <code>Documentation/topics/integration.rst</code> in the Open vSwitch
> > +      source tree) adds the VIF to the OVN integration bridge and stores
> > +      <var>vif-id</var> in <code>external_ids</code>:<code>iface-id</code> 
> > to
> > +      indicate that the interface is an instantiation of the new VIF.  
> > (None of
> > +      this code is new in OVN; this is pre-existing integration work that 
> > has
> > +      already been done on hypervisors that support OVS.)
> >      </li>
> >
> >      <li>
> > diff --git a/tests/automake.mk b/tests/automake.mk
> > index 5b890d644..ad8978541 100644
> > --- a/tests/automake.mk
> > +++ b/tests/automake.mk
> > @@ -38,7 +38,8 @@ TESTSUITE_AT = \
> >         tests/ovn-ipam.at \
> >         tests/ovn-features.at \
> >         tests/ovn-lflow-cache.at \
> > -       tests/ovn-ipsec.at
> > +       tests/ovn-ipsec.at \
> > +       tests/ovn-plug.at
> >
> >  SYSTEM_KMOD_TESTSUITE_AT = \
> >         tests/system-common-macros.at \
> > @@ -248,6 +249,7 @@ tests_ovstest_SOURCES = \
> >         controller/ofctrl-seqno.c \
> >         controller/ofctrl-seqno.h \
> >         lib/test-ovn-features.c \
> > +       lib/test-plug.c \
> >         northd/test-ipam.c \
> >         northd/ipam.c \
> >         northd/ipam.h
> > diff --git a/tests/ovn-plug.at b/tests/ovn-plug.at
> > new file mode 100644
> > index 000000000..d5c6a1b6d
> > --- /dev/null
> > +++ b/tests/ovn-plug.at
> > @@ -0,0 +1,8 @@
> > +#
> > +# Unit tests for the lib/plug.c module.
> > +#
> > +AT_BANNER([OVN unit tests - plug])
> > +
> > +AT_SETUP([unit test -- plugging infrastructure tests])
> > +AT_CHECK([ovstest test-plug run], [0], [])
> > +AT_CLEANUP
> > --
> > 2.32.0
> >
> >
> > _______________________________________________
> > dev mailing list
> > [email protected]
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to