get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/1524524/
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 1524524,
    "url": "http://patchwork.ozlabs.org/api/patches/1524524/",
    "web_url": "http://patchwork.ozlabs.org/project/ovn/patch/20210903192748.1408062-9-frode.nordahl@canonical.com/",
    "project": {
        "id": 68,
        "url": "http://patchwork.ozlabs.org/api/projects/68/",
        "name": "Open Virtual Network development",
        "link_name": "ovn",
        "list_id": "ovs-dev.openvswitch.org",
        "list_email": "ovs-dev@openvswitch.org",
        "web_url": "http://openvswitch.org/",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20210903192748.1408062-9-frode.nordahl@canonical.com>",
    "list_archive_url": null,
    "date": "2021-09-03T19:27:47",
    "name": "[ovs-dev,v4,8/9] binding: Consider plugging of ports on CMS request.",
    "commit_ref": null,
    "pull_url": null,
    "state": "changes-requested",
    "archived": false,
    "hash": "b1e449c6031f87fc55cd523a1ab6768db5e312a9",
    "submitter": {
        "id": 77851,
        "url": "http://patchwork.ozlabs.org/api/people/77851/",
        "name": "Frode Nordahl",
        "email": "frode.nordahl@canonical.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/ovn/patch/20210903192748.1408062-9-frode.nordahl@canonical.com/mbox/",
    "series": [
        {
            "id": 260950,
            "url": "http://patchwork.ozlabs.org/api/series/260950/",
            "web_url": "http://patchwork.ozlabs.org/project/ovn/list/?series=260950",
            "date": "2021-09-03T19:27:42",
            "name": "Introduce infrastructure for plugging providers",
            "version": 4,
            "mbox": "http://patchwork.ozlabs.org/series/260950/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/1524524/comments/",
    "check": "success",
    "checks": "http://patchwork.ozlabs.org/api/patches/1524524/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<ovs-dev-bounces@openvswitch.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "dev@openvswitch.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@bilbo.ozlabs.org",
            "ovs-dev@lists.linuxfoundation.org"
        ],
        "Authentication-Results": [
            "ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n unprotected) header.d=canonical.com header.i=@canonical.com\n header.a=rsa-sha256 header.s=20210705 header.b=hSP/RF6t;\n\tdkim-atps=neutral",
            "ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=2605:bc80:3010::133; helo=smtp2.osuosl.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=<UNKNOWN>)"
        ],
        "Received": [
            "from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest\n SHA256)\n\t(No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 4H1SWR3ZQWz9sCD\n\tfor <incoming@patchwork.ozlabs.org>; Sat,  4 Sep 2021 05:28:19 +1000 (AEST)",
            "from localhost (localhost [127.0.0.1])\n\tby smtp2.osuosl.org (Postfix) with ESMTP id F3FB9408BF;\n\tFri,  3 Sep 2021 19:28:16 +0000 (UTC)",
            "from smtp2.osuosl.org ([127.0.0.1])\n\tby localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)\n\twith ESMTP id 8DizZZAXz0W8; Fri,  3 Sep 2021 19:28:13 +0000 (UTC)",
            "from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56])\n\tby smtp2.osuosl.org (Postfix) with ESMTPS id 7704640871;\n\tFri,  3 Sep 2021 19:28:08 +0000 (UTC)",
            "from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id CC254C0023;\n\tFri,  3 Sep 2021 19:28:01 +0000 (UTC)",
            "from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133])\n by lists.linuxfoundation.org (Postfix) with ESMTP id 595E3C001C\n for <dev@openvswitch.org>; Fri,  3 Sep 2021 19:27:56 +0000 (UTC)",
            "from localhost (localhost [127.0.0.1])\n by smtp2.osuosl.org (Postfix) with ESMTP id 8B7A24027D\n for <dev@openvswitch.org>; Fri,  3 Sep 2021 19:27:55 +0000 (UTC)",
            "from smtp2.osuosl.org ([127.0.0.1])\n by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)\n with ESMTP id YZas-45BrQol for <dev@openvswitch.org>;\n Fri,  3 Sep 2021 19:27:54 +0000 (UTC)",
            "from smtp-relay-canonical-1.canonical.com\n (smtp-relay-canonical-1.canonical.com [185.125.188.121])\n by smtp2.osuosl.org (Postfix) with ESMTPS id 108894070E\n for <dev@openvswitch.org>; Fri,  3 Sep 2021 19:27:53 +0000 (UTC)",
            "from frode-threadripper.. (1.general.frode.uk.vpn [10.172.193.250])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by smtp-relay-canonical-1.canonical.com (Postfix) with ESMTPSA id B3E6540EA3;\n Fri,  3 Sep 2021 19:27:51 +0000 (UTC)"
        ],
        "X-Virus-Scanned": [
            "amavisd-new at osuosl.org",
            "amavisd-new at osuosl.org"
        ],
        "X-Greylist": "domain auto-whitelisted by SQLgrey-1.8.0",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com;\n s=20210705; t=1630697271;\n bh=9i9Ag8TNozoQshNP5+K5SJUHnQRMHy1rr1FdqQaoik4=;\n h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References:\n MIME-Version;\n b=hSP/RF6tYVbPYQJGGjF/EMHs3LJUCz1MdnMTV2k7ixhgiUG08CMyel++GASQJmlS1\n DP92sq4HNpWTwlcaStvkkFsRZ3Krxtnp6PcRVu+50mmcBQe8wY9Q4b5bB3sKr2WWW3\n apPqg5Mis4rcxEJVeTv1zcfHNiFG8eLQQuISxHGW/epz1vZXdsRhhHy2tH7YD1AOVL\n vm3InMYDAiDXrMQkhr/HwkhGEWDQVuwJD/Et3dkAJTB+/ISO9gkQmcI8q++RiXzizH\n MNYnHL5Z36zWZJ4tc+4hJNW5ooogne+rIYuch4TTrnNoAMMaA/wNRzOWWKcGoWswLq\n awPdCHSfLpe3Q==",
        "From": "Frode Nordahl <frode.nordahl@canonical.com>",
        "To": "dev@openvswitch.org",
        "Date": "Fri,  3 Sep 2021 21:27:47 +0200",
        "Message-Id": "<20210903192748.1408062-9-frode.nordahl@canonical.com>",
        "X-Mailer": "git-send-email 2.32.0",
        "In-Reply-To": "<20210903192748.1408062-1-frode.nordahl@canonical.com>",
        "References": "<20210903192748.1408062-1-frode.nordahl@canonical.com>",
        "MIME-Version": "1.0",
        "Subject": "[ovs-dev] [PATCH ovn v4 8/9] binding: Consider plugging of ports on\n\tCMS request.",
        "X-BeenThere": "ovs-dev@openvswitch.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "<ovs-dev.openvswitch.org>",
        "List-Unsubscribe": "<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>",
        "List-Archive": "<http://mail.openvswitch.org/pipermail/ovs-dev/>",
        "List-Post": "<mailto:ovs-dev@openvswitch.org>",
        "List-Help": "<mailto:ovs-dev-request@openvswitch.org?subject=help>",
        "List-Subscribe": "<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=subscribe>",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Errors-To": "ovs-dev-bounces@openvswitch.org",
        "Sender": "\"dev\" <ovs-dev-bounces@openvswitch.org>"
    },
    "content": "When OVN is linked with an appropriate plugging implementation,\nCMS can request OVN to plug individual lports into the local\nOpen vSwitch instance.\n\nThe port and instance record will be maintained during the lifetime\nof the lport and it will be removed on release of lport.\n\nSigned-off-by: Frode Nordahl <frode.nordahl@canonical.com>\n---\n controller/binding.c    | 247 ++++++++++++++++++++++++++++++++++++++--\n tests/ovn-controller.at |  31 +++++\n tests/ovn-macros.at     |   2 +-\n 3 files changed, 272 insertions(+), 8 deletions(-)",
    "diff": "diff --git a/controller/binding.c b/controller/binding.c\nindex 938e1d81d..ae67ee3fc 100644\n--- a/controller/binding.c\n+++ b/controller/binding.c\n@@ -35,7 +35,9 @@\n #include \"local_data.h\"\n #include \"lport.h\"\n #include \"ovn-controller.h\"\n+#include \"ovsport.h\"\n #include \"patch.h\"\n+#include \"plug.h\"\n \n VLOG_DEFINE_THIS_MODULE(binding);\n \n@@ -45,6 +47,8 @@ VLOG_DEFINE_THIS_MODULE(binding);\n  */\n #define OVN_INSTALLED_EXT_ID \"ovn-installed\"\n \n+#define OVN_PLUGGED_EXT_ID \"ovn-plugged\"\n+\n #define OVN_QOS_TYPE \"linux-htb\"\n \n struct qos_queue {\n@@ -71,10 +75,13 @@ binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)\n \n     ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);\n     ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_name);\n+    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_type);\n     ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_external_ids);\n     ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd);\n     ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd_status);\n     ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_status);\n+    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_options);\n+    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_mtu_request);\n \n     ovsdb_idl_add_table(ovs_idl, &ovsrec_table_qos);\n     ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type);\n@@ -1090,6 +1097,51 @@ release_binding_lport(const struct sbrec_chassis *chassis_rec,\n     return true;\n }\n \n+static void\n+consider_unplug_lport(const struct sbrec_port_binding *pb,\n+                      struct binding_ctx_in *b_ctx_in,\n+                      struct local_binding *lbinding)\n+{\n+    const char *plug_type = NULL;\n+    if (lbinding && lbinding->iface) {\n+        plug_type = smap_get(&lbinding->iface->external_ids,\n+                             OVN_PLUGGED_EXT_ID);\n+    }\n+\n+    if (plug_type) {\n+        const struct ovsrec_port *port = ovsport_lookup_by_interface(\n+                b_ctx_in->ovsrec_port_by_interfaces,\n+                (struct ovsrec_interface *) lbinding->iface);\n+        if (port) {\n+            const struct plug_class *plug;\n+            if (!(plug = plug_get_provider(plug_type))) {\n+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n+                VLOG_WARN_RL(&rl,\n+                             \"Unable to open plug provider for \"\n+                             \"plug-type: '%s' lport %s\",\n+                             plug_type, pb->logical_port);\n+                return;\n+            }\n+            const struct plug_port_ctx_in plug_ctx_in = {\n+                    .op_type = PLUG_OP_REMOVE,\n+                    .ovs_table = b_ctx_in->ovs_table,\n+                    .br_int = b_ctx_in->br_int,\n+                    .lport_name = (const char *)pb->logical_port,\n+                    .lport_options = (const struct smap *)&pb->options,\n+                    .iface_name = lbinding->iface->name,\n+                    .iface_type = lbinding->iface->type,\n+                    .iface_options = &lbinding->iface->options,\n+            };\n+            plug_port_prepare(plug, &plug_ctx_in, NULL);\n+            VLOG_INFO(\"Unplugging port %s from %s for lport %s on this \"\n+                      \"chassis.\",\n+                      port->name, b_ctx_in->br_int->name, pb->logical_port);\n+            ovsport_remove(b_ctx_in->br_int, port);\n+            plug_port_finish(plug, &plug_ctx_in, NULL);\n+        }\n+    }\n+}\n+\n static bool\n consider_vif_lport_(const struct sbrec_port_binding *pb,\n                     bool can_bind,\n@@ -1141,15 +1193,184 @@ consider_vif_lport_(const struct sbrec_port_binding *pb,\n     if (pb->chassis == b_ctx_in->chassis_rec) {\n         /* Release the lport if there is no lbinding. */\n         if (!lbinding_set || !can_bind) {\n-            return release_lport(pb, !b_ctx_in->ovnsb_idl_txn,\n-                                 b_ctx_out->tracked_dp_bindings,\n-                                 b_ctx_out->if_mgr);\n+            bool handled = release_lport(pb, !b_ctx_in->ovnsb_idl_txn,\n+                                         b_ctx_out->tracked_dp_bindings,\n+                                         b_ctx_out->if_mgr);\n+            if (handled && b_lport && b_lport->lbinding) {\n+                consider_unplug_lport(pb, b_ctx_in, b_lport->lbinding);\n+            }\n+            return handled;\n         }\n     }\n \n     return true;\n }\n \n+static int64_t\n+get_plug_mtu_request(const struct smap *lport_options)\n+{\n+    return smap_get_int(lport_options, \"plug-mtu-request\", 0);\n+}\n+\n+static bool\n+consider_plug_lport_create__(const struct plug_class *plug,\n+                             const struct smap *iface_external_ids,\n+                             const struct sbrec_port_binding *pb,\n+                             struct binding_ctx_in *b_ctx_in)\n+{\n+    if (!b_ctx_in->chassis_rec || !b_ctx_in->br_int || !b_ctx_in->ovs_idl_txn)\n+    {\n+        /* Some of our prerequisites are not available, ask for a recompute. */\n+        return false;\n+    }\n+\n+    bool ret = true;\n+    struct plug_port_ctx_in plug_ctx_in = {\n+            .op_type = PLUG_OP_CREATE,\n+            .ovs_table = b_ctx_in->ovs_table,\n+            .br_int = b_ctx_in->br_int,\n+            .lport_name = (const char *)pb->logical_port,\n+            .lport_options = (const struct smap *)&pb->options,\n+    };\n+    struct plug_port_ctx_out plug_ctx_out;\n+\n+    if (!plug_port_prepare(plug, &plug_ctx_in, &plug_ctx_out)) {\n+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n+        VLOG_INFO_RL(&rl,\n+                     \"Not plugging lport %s on direction from plugging \"\n+                     \"library.\",\n+                     pb->logical_port);\n+        ret = false;\n+        goto out;\n+    }\n+\n+    VLOG_INFO(\"Plugging port %s into %s for lport %s on this \"\n+              \"chassis.\",\n+              plug_ctx_out.name, b_ctx_in->br_int->name,\n+              pb->logical_port);\n+    ovsport_create(b_ctx_in->ovs_idl_txn, b_ctx_in->br_int,\n+                   plug_ctx_out.name, plug_ctx_out.type,\n+                   NULL, iface_external_ids,\n+                   plug_ctx_out.iface_options,\n+                   get_plug_mtu_request(&pb->options));\n+\n+    plug_port_finish(plug, &plug_ctx_in, &plug_ctx_out);\n+\n+out:\n+    plug_port_ctx_destroy(plug, &plug_ctx_in, &plug_ctx_out);\n+\n+    return ret;\n+}\n+\n+static bool\n+consider_plug_lport_update__(const struct plug_class *plug,\n+                             const struct smap *iface_external_ids,\n+                             const struct sbrec_port_binding *pb,\n+                             struct binding_ctx_in *b_ctx_in,\n+                             struct local_binding *lbinding)\n+{\n+    if (!b_ctx_in->chassis_rec || !b_ctx_in->br_int || !b_ctx_in->ovs_idl_txn)\n+    {\n+        /* Some of our prerequisites are not available, ask for a recompute. */\n+        return false;\n+    }\n+\n+    bool ret = true;\n+    struct plug_port_ctx_in plug_ctx_in = {\n+            .op_type = PLUG_OP_CREATE,\n+            .ovs_table = b_ctx_in->ovs_table,\n+            .br_int = b_ctx_in->br_int,\n+            .lport_name = (const char *)pb->logical_port,\n+            .lport_options = (const struct smap *)&pb->options,\n+            .iface_name = lbinding->iface->name,\n+            .iface_type = lbinding->iface->type,\n+            .iface_options = &lbinding->iface->options,\n+    };\n+    struct plug_port_ctx_out plug_ctx_out;\n+\n+    if (!plug_port_prepare(plug, &plug_ctx_in, &plug_ctx_out)) {\n+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n+        VLOG_INFO_RL(&rl,\n+                     \"Not updating lport %s on direction from plugging \"\n+                     \"library.\",\n+                     pb->logical_port);\n+        ret = false;\n+        goto out;\n+    }\n+\n+    if (strcmp(lbinding->iface->name, plug_ctx_out.name)) {\n+        VLOG_WARN(\"Attempt of incompatible change to existing \"\n+                  \"port detected, please recreate port: %s\",\n+                   pb->logical_port);\n+        ret = false;\n+        goto out;\n+    }\n+    VLOG_DBG(\"updating iface for: %s\", pb->logical_port);\n+    ovsport_update_iface(lbinding->iface, plug_ctx_out.type,\n+                         iface_external_ids,\n+                         NULL,\n+                         plug_ctx_out.iface_options,\n+                         plug_get_maintained_iface_options(plug),\n+                         get_plug_mtu_request(&pb->options));\n+\n+    plug_port_finish(plug, &plug_ctx_in, &plug_ctx_out);\n+out:\n+    plug_port_ctx_destroy(plug, &plug_ctx_in, &plug_ctx_out);\n+\n+    return ret;\n+}\n+\n+static bool\n+consider_plug_lport(const struct sbrec_port_binding *pb,\n+                    struct binding_ctx_in *b_ctx_in,\n+                    struct local_binding *lbinding)\n+{\n+    bool ret = true;\n+    if (can_bind_on_this_chassis(b_ctx_in->chassis_rec, pb)) {\n+        const char *plug_type = smap_get(&pb->options, \"plug-type\");\n+        if (!plug_type) {\n+            /* Nothing for us to do and we don't need a recompute. */\n+            return true;\n+        }\n+\n+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n+        const struct plug_class *plug;\n+        if (!(plug = plug_get_provider(plug_type))) {\n+            VLOG_WARN_RL(&rl,\n+                         \"Unable to open plug provider for plug-type: '%s' \"\n+                         \"lport %s\",\n+                         plug_type, pb->logical_port);\n+            /* While we are unable to handle this, asking for a recompute will\n+             * not change that fact. */\n+            return true;\n+        }\n+        const struct smap iface_external_ids = SMAP_CONST2(\n+                &iface_external_ids,\n+                OVN_PLUGGED_EXT_ID, plug_type,\n+                \"iface-id\", pb->logical_port);\n+        if (lbinding && lbinding->iface) {\n+            if (!smap_get(&lbinding->iface->external_ids,\n+                          OVN_PLUGGED_EXT_ID))\n+            {\n+                VLOG_WARN_RL(&rl,\n+                             \"CMS requested plugging of lport %s, but a port \"\n+                             \"that is not maintained by OVN already exsist \"\n+                             \"in local vSwitch: \"UUID_FMT,\n+                             pb->logical_port,\n+                             UUID_ARGS(&lbinding->iface->header_.uuid));\n+                return false;\n+            }\n+            ret = consider_plug_lport_update__(plug, &iface_external_ids, pb,\n+                                               b_ctx_in, lbinding);\n+        } else {\n+            ret = consider_plug_lport_create__(plug, &iface_external_ids, pb,\n+                                               b_ctx_in);\n+        }\n+    }\n+\n+    return ret;\n+}\n+\n static bool\n consider_vif_lport(const struct sbrec_port_binding *pb,\n                    struct binding_ctx_in *b_ctx_in,\n@@ -1187,8 +1408,13 @@ consider_vif_lport(const struct sbrec_port_binding *pb,\n         }\n     }\n \n-    return consider_vif_lport_(pb, can_bind, b_ctx_in, b_ctx_out,\n-                               b_lport, qos_map);\n+    /* Consider if we should create or update local port/interface record for\n+     * this lport.  Note that a newly created port/interface will get its flows\n+     * installed on the next loop iteration as we won't wait for OVS vSwitchd\n+     * to configure and assign a ofport to the interface. */\n+    return consider_plug_lport(pb, b_ctx_in, lbinding)\n+           & consider_vif_lport_(pb, can_bind, b_ctx_in, b_ctx_out,\n+                                 b_lport, qos_map);\n }\n \n static bool\n@@ -2111,8 +2337,11 @@ handle_deleted_vif_lport(const struct sbrec_port_binding *pb,\n         lbinding = b_lport->lbinding;\n         bound = is_binding_lport_this_chassis(b_lport, b_ctx_in->chassis_rec);\n \n-         /* Remove b_lport from local_binding. */\n-         binding_lport_delete(binding_lports, b_lport);\n+        if (b_lport->lbinding) {\n+            consider_unplug_lport(pb, b_ctx_in, b_lport->lbinding);\n+        }\n+        /* Remove b_lport from local_binding. */\n+        binding_lport_delete(binding_lports, b_lport);\n     }\n \n     if (bound && lbinding && lport_type == LP_VIF) {\n@@ -2690,6 +2919,10 @@ local_binding_handle_stale_binding_lports(struct local_binding *lbinding,\n             handled = release_binding_lport(b_ctx_in->chassis_rec, b_lport,\n                                             !b_ctx_in->ovnsb_idl_txn,\n                                             b_ctx_out);\n+            if (handled && b_lport && b_lport->lbinding) {\n+                consider_unplug_lport(b_lport->pb, b_ctx_in,\n+                                      b_lport->lbinding);\n+            }\n             binding_lport_delete(&b_ctx_out->lbinding_data->lports,\n                                  b_lport);\n         }\ndiff --git a/tests/ovn-controller.at b/tests/ovn-controller.at\nindex 4ae218ed6..a38884374 100644\n--- a/tests/ovn-controller.at\n+++ b/tests/ovn-controller.at\n@@ -723,3 +723,34 @@ AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep controller | grep userdata=0\n OVN_CLEANUP([hv1])\n AT_CLEANUP\n ])\n+\n+OVN_FOR_EACH_NORTHD([\n+AT_SETUP([ovn-controller - plugging])\n+AT_KEYWORDS([plugging])\n+\n+ovn_start\n+\n+net_add n1\n+sim_add hv1\n+ovs-vsctl add-br br-phys\n+ovn_attach n1 br-phys 192.168.0.1\n+\n+check ovn-nbctl ls-add lsw0\n+check ovn-nbctl lsp-add lsw0 lsp1\n+check ovn-nbctl lsp-set-addresses lsp1 \"f0:00:00:00:00:01 172.16.0.100\"\n+check ovn-nbctl --wait=sb set Logical_Switch_Port lsp1 \\\n+    options:requested-chassis=hv1 \\\n+    options:plug-type=dummy \\\n+    options:plug-mtu-request=42\n+\n+wait_column \"true\" Port_Binding up logical_port=lsp1\n+\n+as hv1\n+\n+AT_CHECK([as hv1 ovs-vsctl find interface name=lsp1 options:plug-dummy-option=value | grep -q \"options.*value\"])\n+AT_CHECK([as hv1 ovs-vsctl find interface name=lsp1 mtu_request=42 | grep -q \"mtu_request.*42\"])\n+\n+OVN_CLEANUP([hv1])\n+AT_CLEANUP\n+])\n+\ndiff --git a/tests/ovn-macros.at b/tests/ovn-macros.at\nindex f06f2e68e..b3c2d72fa 100644\n--- a/tests/ovn-macros.at\n+++ b/tests/ovn-macros.at\n@@ -327,7 +327,7 @@ ovn_az_attach() {\n         -- --may-exist add-br br-int \\\n         -- set bridge br-int fail-mode=secure other-config:disable-in-band=true \\\n         || return 1\n-    start_daemon ovn-controller || return 1\n+    start_daemon ovn-controller --enable-dummy-plug || return 1\n }\n \n # ovn_attach NETWORK BRIDGE IP [MASKLEN]\n",
    "prefixes": [
        "ovs-dev",
        "v4",
        "8/9"
    ]
}