get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 787922,
    "url": "http://patchwork.ozlabs.org/api/patches/787922/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/openvswitch/patch/1499965361-32021-3-git-send-email-majopela@redhat.com/",
    "project": {
        "id": 47,
        "url": "http://patchwork.ozlabs.org/api/projects/47/?format=api",
        "name": "Open vSwitch",
        "link_name": "openvswitch",
        "list_id": "ovs-dev.openvswitch.org",
        "list_email": "ovs-dev@openvswitch.org",
        "web_url": "http://openvswitch.org/",
        "scm_url": "git@github.com:openvswitch/ovs.git",
        "webscm_url": "https://github.com/openvswitch/ovs",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<1499965361-32021-3-git-send-email-majopela@redhat.com>",
    "list_archive_url": null,
    "date": "2017-07-13T17:02:36",
    "name": "[ovs-dev,v5,3/8] ovn: l3ha, ovn-northd gateway chassis propagation",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "b6447ebba986100081aae54184408ae95f75d7d6",
    "submitter": {
        "id": 67170,
        "url": "http://patchwork.ozlabs.org/api/people/67170/?format=api",
        "name": "Miguel Angel Ajo",
        "email": "majopela@redhat.com"
    },
    "delegate": {
        "id": 55721,
        "url": "http://patchwork.ozlabs.org/api/users/55721/?format=api",
        "username": "russellb",
        "first_name": "Russell",
        "last_name": "Bryant",
        "email": "rbryant@redhat.com"
    },
    "mbox": "http://patchwork.ozlabs.org/project/openvswitch/patch/1499965361-32021-3-git-send-email-majopela@redhat.com/mbox/",
    "series": [],
    "comments": "http://patchwork.ozlabs.org/api/patches/787922/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/787922/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@mail.linuxfoundation.org"
        ],
        "Received": [
            "from mail.linuxfoundation.org (mail.linuxfoundation.org\n\t[140.211.169.12])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256\n\tbits)) (No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3x7hxK0xNqz9t2S\n\tfor <incoming@patchwork.ozlabs.org>;\n\tFri, 14 Jul 2017 03:04:05 +1000 (AEST)",
            "from mail.linux-foundation.org (localhost [127.0.0.1])\n\tby mail.linuxfoundation.org (Postfix) with ESMTP id 66697B75;\n\tThu, 13 Jul 2017 17:02:54 +0000 (UTC)",
            "from smtp1.linuxfoundation.org (smtp1.linux-foundation.org\n\t[172.17.192.35])\n\tby mail.linuxfoundation.org (Postfix) with ESMTPS id 897CCB6E\n\tfor <dev@openvswitch.org>; Thu, 13 Jul 2017 17:02:53 +0000 (UTC)",
            "from mail-wm0-f46.google.com (mail-wm0-f46.google.com\n\t[74.125.82.46])\n\tby smtp1.linuxfoundation.org (Postfix) with ESMTPS id 5953FCD\n\tfor <dev@openvswitch.org>; Thu, 13 Jul 2017 17:02:51 +0000 (UTC)",
            "by mail-wm0-f46.google.com with SMTP id f67so32634618wmh.1\n\tfor <dev@openvswitch.org>; Thu, 13 Jul 2017 10:02:51 -0700 (PDT)",
            "from ctl.localdomain (111.148.134.37.dynamic.jazztel.es.\n\t[37.134.148.111]) by smtp.gmail.com with ESMTPSA id\n\t46sm7134176wrz.8.2017.07.13.10.02.46\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);\n\tThu, 13 Jul 2017 10:02:47 -0700 (PDT)"
        ],
        "X-Greylist": "whitelisted by SQLgrey-1.7.6",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references;\n\tbh=ZgsBpVOpa4qyP8Mh7NYw24R0Lg8dfWoqrKT40/qEKek=;\n\tb=sojvrdwb9UBjHijHnI+KV8N8HBXW6f+Kuat5dID2Vev6j2lUGq2K5GemRJj8NnzxJo\n\tcJUPkrkmYHmmmmn7B+y1ME6XxFnO/SafHaXhshSBkNc6zqXryntB3hu+nVWzGiEgyjPD\n\tegr+JyPxPUIbFpyqHl0pk3D4vAJt3jz9BuQLkyz19FS2SH9MErPi4GdFV8+oStjoel+w\n\tefNlf8t8eNaQnXV7OAXvBDp+Pwi4tSa6vm8Db2p8oacD4tSMtLrmhUtcbb0lZUd4lBj6\n\t00hB8sQiR4Jd4C7qQ17Ar46OFsKG8/OgAzra7s+WSpjro9KvYH1VDtMIRUTehfgPNoBW\n\tyI4g==",
        "X-Gm-Message-State": "AIVw110/gl5XlutihSH3ODPHgFO4JgnBZe1TKF8NK7TcIOX4hkr6jj3b\n\tpR8NYIVF8LrULvg379a1ng==",
        "X-Received": "by 10.28.95.137 with SMTP id t131mr2844259wmb.102.1499965369045; \n\tThu, 13 Jul 2017 10:02:49 -0700 (PDT)",
        "From": "Miguel Angel Ajo <majopela@redhat.com>",
        "To": "dev@openvswitch.org",
        "Date": "Thu, 13 Jul 2017 17:02:36 +0000",
        "Message-Id": "<1499965361-32021-3-git-send-email-majopela@redhat.com>",
        "X-Mailer": "git-send-email 1.8.3.1",
        "In-Reply-To": "<1499965361-32021-1-git-send-email-majopela@redhat.com>",
        "References": "<1496930708-15441-1-git-send-email-majopela@redhat.com>\n\t<1499965361-32021-1-git-send-email-majopela@redhat.com>",
        "X-Spam-Status": "No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE\n\tautolearn=ham version=3.3.1",
        "X-Spam-Checker-Version": "SpamAssassin 3.3.1 (2010-03-16) on\n\tsmtp1.linux-foundation.org",
        "Cc": "\"majopela@redhat.com\" <majopela@redhat.com>",
        "Subject": "[ovs-dev] [PATCH v5 3/8] ovn: l3ha,\n\tovn-northd gateway chassis propagation",
        "X-BeenThere": "ovs-dev@openvswitch.org",
        "X-Mailman-Version": "2.1.12",
        "Precedence": "list",
        "List-Id": "<ovs-dev.openvswitch.org>",
        "List-Unsubscribe": "<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n\t<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\t<mailto:ovs-dev-request@openvswitch.org?subject=subscribe>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Sender": "ovs-dev-bounces@openvswitch.org",
        "Errors-To": "ovs-dev-bounces@openvswitch.org"
    },
    "content": "From: \"majopela@redhat.com\" <majopela@redhat.com>\n\nThe redirect-chassis option of logical router ports is now\ntranslated to Gateway_Chassis entries for backwards compatibility.\n\nGateway_Chassis entries in nbdb are copied over to sbdb and\nlinked them to the Chassis entry.\n\nCo-authored-by: Anil Venkata <vkommadi@redhat.com>\nSigned-off-by: Miguel Angel Ajo <majopela@redhat.com>\nSigned-off-by: Anil Venkata <vkommadi@redhat.com>\n---\n ovn/lib/automake.mk     |   2 +\n ovn/lib/chassis-index.c |  77 +++++++++++++++\n ovn/lib/chassis-index.h |  40 ++++++++\n ovn/northd/ovn-northd.c | 249 ++++++++++++++++++++++++++++++++++++++++++++++--\n tests/automake.mk       |   1 +\n tests/ovn-northd.at     |  87 +++++++++++++++++\n tests/testsuite.at      |   1 +\n 7 files changed, 448 insertions(+), 9 deletions(-)\n create mode 100644 ovn/lib/chassis-index.c\n create mode 100644 ovn/lib/chassis-index.h\n create mode 100644 tests/ovn-northd.at",
    "diff": "diff --git a/ovn/lib/automake.mk b/ovn/lib/automake.mk\nindex b86237e..9ad8b6a 100644\n--- a/ovn/lib/automake.mk\n+++ b/ovn/lib/automake.mk\n@@ -5,6 +5,8 @@ ovn_lib_libovn_la_LDFLAGS = \\\n         $(AM_LDFLAGS)\n ovn_lib_libovn_la_SOURCES = \\\n \tovn/lib/actions.c \\\n+\tovn/lib/chassis-index.c \\\n+\tovn/lib/chassis-index.h \\\n \tovn/lib/expr.c \\\n \tovn/lib/lex.c \\\n \tovn/lib/ovn-dhcp.h \\\ndiff --git a/ovn/lib/chassis-index.c b/ovn/lib/chassis-index.c\nnew file mode 100644\nindex 0000000..ae65375\n--- /dev/null\n+++ b/ovn/lib/chassis-index.c\n@@ -0,0 +1,77 @@\n+/* Copyright (c) 2016, 2017 Red Hat, Inc.\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at:\n+ *\n+ *     http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+#include <config.h>\n+\n+#include \"openvswitch/hmap.h\"\n+#include \"openvswitch/vlog.h\"\n+#include \"ovn/actions.h\"\n+#include \"ovn/lib/chassis-index.h\"\n+#include \"ovn/lib/ovn-sb-idl.h\"\n+\n+VLOG_DEFINE_THIS_MODULE(chassis_index);\n+\n+struct chassis {\n+    struct hmap_node name_node;\n+    const struct sbrec_chassis *db;\n+};\n+\n+const struct sbrec_chassis *\n+chassis_lookup_by_name(const struct chassis_index *chassis_index,\n+                       const char *name)\n+{\n+    const struct chassis *chassis;\n+    HMAP_FOR_EACH_WITH_HASH (chassis, name_node, hash_string(name, 0),\n+                             &chassis_index->by_name) {\n+        if (!strcmp(chassis->db->name, name)) {\n+            return chassis->db;\n+        }\n+    }\n+    return NULL;\n+}\n+\n+void\n+chassis_index_init(struct chassis_index *chassis_index,\n+                   struct ovsdb_idl *sb_idl)\n+{\n+    hmap_init(&chassis_index->by_name);\n+\n+    const struct sbrec_chassis *chassis;\n+    SBREC_CHASSIS_FOR_EACH (chassis, sb_idl) {\n+        if (!chassis->name) {\n+            continue;\n+        }\n+        struct chassis *c = xmalloc(sizeof *c);\n+        hmap_insert(&chassis_index->by_name, &c->name_node,\n+                    hash_string(chassis->name, 0));\n+        c->db = chassis;\n+    }\n+}\n+\n+void\n+chassis_index_destroy(struct chassis_index *chassis_index)\n+{\n+    if (!chassis_index) {\n+        return;\n+    }\n+\n+    /* Destroy all of the \"struct chassis\"s. */\n+    struct chassis *chassis, *next;\n+    HMAP_FOR_EACH_SAFE (chassis, next, name_node, &chassis_index->by_name) {\n+        hmap_remove(&chassis_index->by_name, &chassis->name_node);\n+        free(chassis);\n+    }\n+\n+    hmap_destroy(&chassis_index->by_name);\n+}\ndiff --git a/ovn/lib/chassis-index.h b/ovn/lib/chassis-index.h\nnew file mode 100644\nindex 0000000..a490cbb\n--- /dev/null\n+++ b/ovn/lib/chassis-index.h\n@@ -0,0 +1,40 @@\n+/* Copyright (c) 2017, Red Hat, Inc.\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at:\n+ *\n+ *     http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+#ifndef OVN_CHASSIS_INDEX_H\n+#define OVN_CHASSIS_INDEX_H 1\n+\n+#include \"openvswitch/hmap.h\"\n+\n+struct chassis_index {\n+    struct hmap by_name;\n+};\n+\n+struct ovsdb_idl;\n+\n+/* Finds and returns the chassis with the given 'name', or NULL if no such\n+ * chassis exists. */\n+const struct sbrec_chassis *\n+chassis_lookup_by_name(const struct chassis_index *chassis_index,\n+                       const char *name);\n+\n+/* Initializes the chassis index out of the ovsdb_idl to SBDB */\n+void chassis_index_init(struct chassis_index *chassis_index,\n+                        struct ovsdb_idl *sb_idl);\n+\n+/* Free a chassis index from memory */\n+void chassis_index_destroy(struct chassis_index *chassis_index);\n+\n+#endif /* ovn/lib/chassis-index.h */\ndiff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c\nindex be3b371..990d268 100644\n--- a/ovn/northd/ovn-northd.c\n+++ b/ovn/northd/ovn-northd.c\n@@ -28,6 +28,7 @@\n #include \"openvswitch/hmap.h\"\n #include \"openvswitch/json.h\"\n #include \"ovn/lex.h\"\n+#include \"ovn/lib/chassis-index.h\"\n #include \"ovn/lib/logical-fields.h\"\n #include \"ovn/lib/ovn-dhcp.h\"\n #include \"ovn/lib/ovn-nb-idl.h\"\n@@ -1453,7 +1454,7 @@ join_logical_ports(struct northd_context *ctx,\n \n                 const char *redirect_chassis = smap_get(&op->nbrp->options,\n                                                         \"redirect-chassis\");\n-                if (redirect_chassis) {\n+                if (redirect_chassis || op->nbrp->n_gateway_chassis) {\n                     /* Additional \"derived\" ovn_port crp represents the\n                      * instance of op on the \"redirect-chassis\". */\n                     const char *gw_chassis = smap_get(&op->od->nbr->options,\n@@ -1678,8 +1679,159 @@ get_nat_addresses(const struct ovn_port *op, size_t *n)\n     return addresses;\n }\n \n+static bool\n+gateway_chassis_equal(const struct nbrec_gateway_chassis *nb_gwc,\n+                      const struct sbrec_chassis *nb_gwc_c,\n+                      const struct sbrec_gateway_chassis *sb_gwc)\n+{\n+    return !strcmp(nb_gwc->name, sb_gwc->name)\n+           && !strcmp(nb_gwc_c->name, sb_gwc->chassis->name)\n+           && nb_gwc->priority == sb_gwc->priority\n+           && smap_equal(&nb_gwc->options, &sb_gwc->options)\n+           && smap_equal(&nb_gwc->external_ids, &sb_gwc->external_ids);\n+}\n+\n+static bool\n+sbpb_gw_chassis_needs_update(\n+        const struct sbrec_port_binding *port_binding,\n+        const struct nbrec_logical_router_port *lrp,\n+        const struct chassis_index *chassis_index)\n+{\n+    if (!lrp || !port_binding) {\n+        return false;\n+    }\n+\n+    /* These arrays are used to collect valid Gateway_Chassis and valid\n+     * Chassis records from the Logical_Router_Port Gateway_Chassis list,\n+     * we ignore the ones we can't match on the SBDB */\n+    struct nbrec_gateway_chassis **lrp_gwc = xzalloc(lrp->n_gateway_chassis *\n+                                                     sizeof *lrp_gwc);\n+    const struct sbrec_chassis **lrp_gwc_c = xzalloc(lrp->n_gateway_chassis *\n+                                               sizeof *lrp_gwc_c);\n+\n+    /* Count the number of gateway chassis chassis names from the logical\n+     * router port that we are able to match on the southbound database */\n+    int lrp_n_gateway_chassis = 0;\n+    int n;\n+    for (n = 0; n < lrp->n_gateway_chassis; n++) {\n+\n+        if (!lrp->gateway_chassis[n]->chassis_name) {\n+            continue;\n+        }\n+\n+        const struct sbrec_chassis *chassis =\n+            chassis_lookup_by_name(chassis_index,\n+                                   lrp->gateway_chassis[n]->chassis_name);\n+\n+        if (chassis) {\n+            lrp_gwc_c[lrp_n_gateway_chassis] = chassis;\n+            lrp_gwc[lrp_n_gateway_chassis] = lrp->gateway_chassis[n];\n+            lrp_n_gateway_chassis++;\n+        } else {\n+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);\n+            VLOG_WARN_RL(\n+                &rl, \"Chassis name %s referenced in NBDB via Gateway_Chassis \"\n+                     \"on logical router port %s does not exist in SBDB\",\n+                     lrp->gateway_chassis[n]->chassis_name, lrp->name);\n+\n+        }\n+\n+    }\n+\n+    /* Basic check, different amount of Gateway_Chassis means that we\n+     * need to update southbound database Port_Binding */\n+    if (lrp_n_gateway_chassis != port_binding->n_gateway_chassis) {\n+        free(lrp_gwc_c);\n+        free(lrp_gwc);\n+        return true;\n+    }\n+\n+    for (n = 0; n < lrp_n_gateway_chassis; n++) {\n+        int i;\n+        /* For each of the valid gw chassis on the lrp, check if there's\n+         * a match on the Port_Binding list, we assume order is not\n+         * persisted */\n+        for (i = 0; i < port_binding->n_gateway_chassis; i++) {\n+            if (gateway_chassis_equal(lrp_gwc[n],\n+                                      lrp_gwc_c[n],\n+                                      port_binding->gateway_chassis[i])) {\n+                break; /* we found a match */\n+            }\n+        }\n+\n+        /* if no Port_Binding gateway chassis matched for the entry... */\n+        if (i == port_binding->n_gateway_chassis) {\n+            free(lrp_gwc_c);\n+            free(lrp_gwc);\n+            return true; /* found no match for this gateway chassis on lrp */\n+        }\n+    }\n+\n+    /* no need for update, all ports matched */\n+    free(lrp_gwc_c);\n+    free(lrp_gwc);\n+    return false;\n+}\n+\n+/* This functions translates the gw chassis on the nb database\n+ * to sb database entries, the only difference is that SB database\n+ * Gateway_Chassis table references the chassis directly instead\n+ * of using the name */\n+static void\n+copy_gw_chassis_from_nbrp_to_sbpb(\n+        struct northd_context *ctx,\n+        const struct nbrec_logical_router_port *lrp,\n+        const struct chassis_index *chassis_index,\n+        const struct sbrec_port_binding *port_binding) {\n+\n+    if (!lrp || !port_binding || !lrp->n_gateway_chassis) {\n+        return;\n+    }\n+\n+    struct sbrec_gateway_chassis **gw_chassis = NULL;\n+    int n_gwc = 0;\n+    int n;\n+\n+    /* XXX: This can be improved. This code will generate a set of new\n+     * Gateway_Chassis and push them all in a single transaction, instead\n+     * this would be more optimal if we just add/update/remove the rows in\n+     * the southbound db that need to change. We don't expect lots of\n+     * changes to the Gateway_Chassis table, but if that proves to be wrong\n+     * we should optimize this. */\n+    for (n = 0; n < lrp->n_gateway_chassis; n++) {\n+        struct nbrec_gateway_chassis *lrp_gwc = lrp->gateway_chassis[n];\n+        if (!lrp_gwc->chassis_name) {\n+            continue;\n+        }\n+\n+        const struct sbrec_chassis *chassis =\n+            chassis_lookup_by_name(chassis_index, lrp_gwc->chassis_name);\n+\n+        if (!chassis) {\n+            continue;\n+        }\n+\n+        gw_chassis = xrealloc(gw_chassis, (n_gwc + 1) * sizeof *gw_chassis);\n+\n+        struct sbrec_gateway_chassis *pb_gwc =\n+            sbrec_gateway_chassis_insert(ctx->ovnsb_txn);\n+\n+        sbrec_gateway_chassis_set_name(pb_gwc, lrp_gwc->name);\n+        sbrec_gateway_chassis_set_priority(pb_gwc, lrp_gwc->priority);\n+        sbrec_gateway_chassis_set_chassis(pb_gwc, chassis);\n+        sbrec_gateway_chassis_set_options(pb_gwc, &lrp_gwc->options);\n+        sbrec_gateway_chassis_set_external_ids(pb_gwc, &lrp_gwc->external_ids);\n+\n+        gw_chassis[n_gwc++] = pb_gwc;\n+    }\n+    sbrec_port_binding_set_gateway_chassis(port_binding, gw_chassis, n_gwc);\n+    free(gw_chassis);\n+}\n+\n static void\n-ovn_port_update_sbrec(const struct ovn_port *op,\n+ovn_port_update_sbrec(struct northd_context *ctx,\n+                      const struct ovn_port *op,\n+                      const struct chassis_index *chassis_index,\n                       struct hmap *chassis_qdisc_queues)\n {\n     sbrec_port_binding_set_datapath(op->sb, op->od->sb);\n@@ -1700,8 +1852,69 @@ ovn_port_update_sbrec(const struct ovn_port *op,\n         if (op->derived) {\n             const char *redirect_chassis = smap_get(&op->nbrp->options,\n                                                     \"redirect-chassis\");\n-            if (redirect_chassis) {\n+            if (op->nbrp->n_gateway_chassis && redirect_chassis) {\n+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);\n+                VLOG_WARN_RL(\n+                    &rl, \"logical router port %s has both options:\"\n+                         \"redirect-chassis and gateway_chassis populated \"\n+                         \"redirect-chassis will be ignored in favour of \"\n+                         \"gateway chassis\", op->nbrp->name);\n+            }\n+\n+            if (op->nbrp->n_gateway_chassis) {\n+                if (sbpb_gw_chassis_needs_update(op->sb, op->nbrp,\n+                                                   chassis_index)) {\n+                    copy_gw_chassis_from_nbrp_to_sbpb(ctx, op->nbrp,\n+                                                        chassis_index, op->sb);\n+                }\n+\n+            } else if (redirect_chassis) {\n+                /* XXX: Keep the \"redirect-chassis\" option on the Port_Binding\n+                 * for compatibility purposes until ovn-controller implements\n+                 * Gateway_Chassis handling */\n                 smap_add(&new, \"redirect-chassis\", redirect_chassis);\n+\n+                /* Handle ports that had redirect-chassis option attached\n+                 * to them for backwards compatibility */\n+                const struct sbrec_chassis *chassis =\n+                    chassis_lookup_by_name(chassis_index, redirect_chassis);\n+                if (chassis) {\n+                    /* If we found the chassis, and the gw chassis on record\n+                     * differs from what we expect go ahead and update */\n+                    if (op->sb->n_gateway_chassis !=1\n+                        || strcmp(op->sb->gateway_chassis[0]->chassis->name,\n+                                  chassis->name)\n+                        || op->sb->gateway_chassis[0]->priority != 0) {\n+                        /* Construct a single Gateway_Chassis entry on the\n+                         * Port_Binding attached to the redirect_chassis\n+                         * name */\n+                        struct sbrec_gateway_chassis *gw_chassis =\n+                            sbrec_gateway_chassis_insert(ctx->ovnsb_txn);\n+\n+                        char *gwc_name = xasprintf(\"%s_%s\", op->nbrp->name,\n+                                chassis->name);\n+\n+                        /* XXX: Again, here, we could just update an existing\n+                         * Gateway_Chassis, instead of creating a new one\n+                         * and replacing it */\n+                        sbrec_gateway_chassis_set_name(gw_chassis, gwc_name);\n+                        sbrec_gateway_chassis_set_priority(gw_chassis, 0);\n+                        sbrec_gateway_chassis_set_chassis(gw_chassis, chassis);\n+                        sbrec_gateway_chassis_set_external_ids(gw_chassis,\n+                                &op->nbrp->external_ids);\n+                        sbrec_port_binding_set_gateway_chassis(op->sb,\n+                                                               &gw_chassis, 1);\n+                        free(gwc_name);\n+                    }\n+                } else {\n+                    VLOG_WARN(\"chassis name '%s' from redirect from logical \"\n+                              \" router port '%s' redirect-chassis not found\",\n+                              redirect_chassis, op->nbrp->name);\n+                    if (op->sb->n_gateway_chassis) {\n+                        sbrec_port_binding_set_gateway_chassis(op->sb, NULL,\n+                                                               0);\n+                    }\n+                }\n             }\n             smap_add(&new, \"distributed-port\", op->nbrp->name);\n         } else {\n@@ -1845,7 +2058,7 @@ cleanup_mac_bindings(struct northd_context *ctx, struct hmap *ports)\n  * datapaths. */\n static void\n build_ports(struct northd_context *ctx, struct hmap *datapaths,\n-            struct hmap *ports)\n+            const struct chassis_index *chassis_index, struct hmap *ports)\n {\n     struct ovs_list sb_only, nb_only, both;\n     struct hmap tag_alloc_table = HMAP_INITIALIZER(&tag_alloc_table);\n@@ -1863,7 +2076,7 @@ build_ports(struct northd_context *ctx, struct hmap *datapaths,\n         if (op->nbsp) {\n             tag_alloc_create_new_tag(&tag_alloc_table, op->nbsp);\n         }\n-        ovn_port_update_sbrec(op, &chassis_qdisc_queues);\n+        ovn_port_update_sbrec(ctx, op, chassis_index, &chassis_qdisc_queues);\n \n         add_tnlid(&op->od->port_tnlids, op->sb->tunnel_key);\n         if (op->sb->tunnel_key > op->od->port_key_hint) {\n@@ -1879,7 +2092,7 @@ build_ports(struct northd_context *ctx, struct hmap *datapaths,\n         }\n \n         op->sb = sbrec_port_binding_insert(ctx->ovnsb_txn);\n-        ovn_port_update_sbrec(op, &chassis_qdisc_queues);\n+        ovn_port_update_sbrec(ctx, op, chassis_index, &chassis_qdisc_queues);\n \n         sbrec_port_binding_set_logical_port(op->sb, op->key);\n         sbrec_port_binding_set_tunnel_key(op->sb, tunnel_key);\n@@ -5606,14 +5819,15 @@ sync_dns_entries(struct northd_context *ctx, struct hmap *datapaths)\n \n \f\n static void\n-ovnnb_db_run(struct northd_context *ctx, struct ovsdb_idl_loop *sb_loop)\n+ovnnb_db_run(struct northd_context *ctx, struct chassis_index *chassis_index,\n+             struct ovsdb_idl_loop *sb_loop)\n {\n     if (!ctx->ovnsb_txn || !ctx->ovnnb_txn) {\n         return;\n     }\n     struct hmap datapaths, ports;\n     build_datapaths(ctx, &datapaths);\n-    build_ports(ctx, &datapaths, &ports);\n+    build_ports(ctx, &datapaths, chassis_index, &ports);\n     build_ipam(&datapaths, &ports);\n     build_lflows(ctx, &datapaths, &ports);\n \n@@ -6183,6 +6397,17 @@ main(int argc, char *argv[])\n     add_column_noalert(ovnsb_idl_loop.idl,\n                        &sbrec_port_binding_col_nat_addresses);\n     ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_port_binding_col_chassis);\n+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n+                         &sbrec_port_binding_col_gateway_chassis);\n+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n+                         &sbrec_gateway_chassis_col_chassis);\n+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_gateway_chassis_col_name);\n+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n+                         &sbrec_gateway_chassis_col_priority);\n+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n+                         &sbrec_gateway_chassis_col_external_ids);\n+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n+                         &sbrec_gateway_chassis_col_options);\n     add_column_noalert(ovnsb_idl_loop.idl,\n                        &sbrec_port_binding_col_external_ids);\n     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_mac_binding);\n@@ -6223,6 +6448,7 @@ main(int argc, char *argv[])\n \n     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis);\n     ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);\n+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_name);\n \n     /* Main loop. */\n     exiting = false;\n@@ -6234,7 +6460,10 @@ main(int argc, char *argv[])\n             .ovnsb_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),\n         };\n \n-        ovnnb_db_run(&ctx, &ovnsb_idl_loop);\n+        struct chassis_index chassis_index;\n+        chassis_index_init(&chassis_index, ctx.ovnsb_idl);\n+\n+        ovnnb_db_run(&ctx, &chassis_index, &ovnsb_idl_loop);\n         ovnsb_db_run(&ctx, &ovnsb_idl_loop);\n         if (ctx.ovnsb_txn) {\n             check_and_add_supported_dhcp_opts_to_sb_db(&ctx);\n@@ -6254,6 +6483,8 @@ main(int argc, char *argv[])\n         if (should_service_stop()) {\n             exiting = true;\n         }\n+\n+        chassis_index_destroy(&chassis_index);\n     }\n \n     unixctl_server_destroy(unixctl);\ndiff --git a/tests/automake.mk b/tests/automake.mk\nindex 3118bbc..58520a8 100644\n--- a/tests/automake.mk\n+++ b/tests/automake.mk\n@@ -94,6 +94,7 @@ TESTSUITE_AT = \\\n \ttests/vtep-ctl.at \\\n \ttests/auto-attach.at \\\n \ttests/ovn.at \\\n+\ttests/ovn-northd.at \\\n \ttests/ovn-nbctl.at \\\n \ttests/ovn-sbctl.at \\\n \ttests/ovn-controller.at \\\ndiff --git a/tests/ovn-northd.at b/tests/ovn-northd.at\nnew file mode 100644\nindex 0000000..d0e8bcd\n--- /dev/null\n+++ b/tests/ovn-northd.at\n@@ -0,0 +1,87 @@\n+AT_BANNER([OVN northd])\n+AT_SETUP([ovn -- check Gateway_Chassis propagation from NBDB to SBDB])\n+AT_SKIP_IF([test $HAVE_PYTHON = no])\n+ovn_start\n+\n+ovn-nbctl create Logical_Router name=R1\n+ovn-sbctl chassis-add gw1 geneve 127.0.0.1\n+ovn-sbctl chassis-add gw2 geneve 1.2.4.8\n+\n+# Connect alice to R1 as distributed router gateway port on hv2\n+ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24\n+\n+ovn-nbctl --wait=sb \\\n+    --id=@gc0 create Gateway_Chassis name=alice_gw1 \\\n+                                     chassis_name=gw1 \\\n+                                     priority=20 -- \\\n+    --id=@gc1 create Gateway_Chassis name=alice_gw2 \\\n+                                     chassis_name=gw2 \\\n+                                     priority=10 -- \\\n+    set Logical_Router_Port alice 'gateway_chassis=[@gc0,@gc1]'\n+\n+nb_gwc1_uuid=`ovn-nbctl --bare --columns _uuid find Gateway_Chassis name=\"alice_gw1\"`\n+gwc1_uuid=`ovn-sbctl --bare --columns _uuid find Gateway_Chassis name=\"alice_gw1\"`\n+gwc2_uuid=`ovn-sbctl --bare --columns _uuid find Gateway_Chassis name=\"alice_gw2\"`\n+\n+\n+echo \"Port_Binding for cr-alice:\"\n+ovn-sbctl find port_binding logical_port=\"cr-alice\"\n+echo \"Gateway_Chassis list:\"\n+ovn-sbctl list Gateway_Chassis\n+\n+AT_CHECK([ovn-sbctl --bare --columns gateway_chassis find port_binding logical_port=\"cr-alice\" | grep $gwc1_uuid | wc -l], [0], [1\n+])\n+AT_CHECK([ovn-sbctl --bare --columns gateway_chassis find port_binding logical_port=\"cr-alice\" | grep $gwc2_uuid | wc -l], [0], [1\n+])\n+\n+# delete the 2nd Gateway_Chassis on NBDB for alice port\n+\n+ovn-nbctl --wait=sb set Logical_Router_Port alice gateway_chassis=${nb_gwc1_uuid}\n+\n+gwc1_uuid=`ovn-sbctl --bare --columns _uuid find Gateway_Chassis name=\"alice_gw1\"`\n+\n+AT_CHECK([ovn-sbctl --bare --columns gateway_chassis find port_binding logical_port=\"cr-alice\" | grep $gwc1_uuid | wc -l], [0], [1\n+])\n+\n+AT_CHECK([ovn-sbctl find Gateway_Chassis name=alice_gw2], [0], [])\n+\n+# delete all the gateway_chassis on NBDB for alice port\n+\n+ovn-nbctl --wait=sb clear Logical_Router_Port alice gateway_chassis\n+\n+# expect that the Gateway_Chassis doesn't exist anymore\n+\n+AT_CHECK([ovn-sbctl find Gateway_Chassis name=alice_gw1], [0], [])\n+AT_CHECK([ovn-sbctl find Gateway_Chassis name=alice_gw2], [0], [])\n+\n+AT_CLEANUP\n+\n+AT_SETUP([ovn -- check Gateway_Chassis propagation from NBDB to SBDB backwards compatibility])\n+AT_SKIP_IF([test $HAVE_PYTHON = no])\n+ovn_start\n+\n+ovn-nbctl create Logical_Router name=R1\n+ovn-sbctl chassis-add gw1 geneve 127.0.0.1\n+ovn-sbctl chassis-add gw2 geneve 1.2.4.8\n+\n+ovn-nbctl --wait=sb lrp-add R1 bob 00:00:02:01:02:03 172.16.1.1/24 \\\n+    -- set Logical_Router_Port bob options:redirect-chassis=\"gw1\"\n+\n+\n+# It should be converted to Gateway_Chassis entries in SBDB, and\n+# still redirect-chassis is kept for backwards compatibility\n+\n+gwc1_uuid=`ovn-sbctl --bare --columns _uuid find Gateway_Chassis name=\"bob_gw1\"`\n+\n+AT_CHECK([ovn-sbctl --bare --columns options find port_binding logical_port=\"cr-bob\" | grep 'redirect-chassis=gw1' | wc -l], [0], [1\n+])\n+AT_CHECK([ovn-sbctl --bare --columns gateway_chassis find port_binding logical_port=\"cr-bob\" | grep $gwc1_uuid | wc -l], [0], [1\n+])\n+\n+ovn-nbctl --wait=sb remove Logical_Router_Port bob options redirect-chassis\n+\n+# expect that the Gateway_Chassis doesn't exist anymore\n+\n+AT_CHECK([ovn-sbctl find Gateway_Chassis name=bob_gw1], [0], [])\n+\n+AT_CLEANUP\ndiff --git a/tests/testsuite.at b/tests/testsuite.at\nindex c7febc6..3e2eb6d 100644\n--- a/tests/testsuite.at\n+++ b/tests/testsuite.at\n@@ -72,6 +72,7 @@ m4_include([tests/vlog.at])\n m4_include([tests/vtep-ctl.at])\n m4_include([tests/auto-attach.at])\n m4_include([tests/ovn.at])\n+m4_include([tests/ovn-northd.at])\n m4_include([tests/ovn-nbctl.at])\n m4_include([tests/ovn-sbctl.at])\n m4_include([tests/ovn-controller.at])\n",
    "prefixes": [
        "ovs-dev",
        "v5",
        "3/8"
    ]
}