At the moment ovs meters are reconfigured by ovn just when a
meter is allocated or removed while updates for an already
allocated meter are ignored. This issue can be easily verified
with the following reproducer:

$ovn-nbctl meter-add meter0 drop 10 pktps
$ovn-nbctl --log --meter=meter0 acl-add sw0 to-lport 1000 'tcp.dst == 80' drop
$ovn-nbctl --may-exist meter-add meter0 drop 20 pktps
$ovs-ofctl -O OpenFlow15 dump-meters br-int

In order to fix the issue introduce incremental processing for ovn
metering configured through sb db meter/meter-band tables and remove
meter extend table management (extend table is now considering just
meters created through the set_meter() action since we do not have
an entry in the sb db for them).

Related bz: https://bugzilla.redhat.com/show_bug.cgi?id=1939524
Acked-by: Mark Michelson <mmich...@redhat.com>

Signed-off-by: Lorenzo Bianconi <lorenzo.bianc...@redhat.com>
---
 controller/lflow.c          |  28 +----
 controller/lflow.h          |   1 +
 controller/ofctrl.c         |  34 ++----
 controller/ofctrl.h         |   6 +-
 controller/ovn-controller.c | 230 +++++++++++++++++++++++++++++++++++-
 include/ovn/actions.h       |   1 +
 lib/actions.c               |   7 +-
 lib/ovn-util.c              |   9 ++
 lib/ovn-util.h              |  15 +++
 tests/ovn-controller.at     |   6 +-
 tests/ovn-performance.at    |  22 ++++
 tests/ovn.at                |  69 ++++++++++-
 tests/system-ovn.at         |  17 +++
 13 files changed, 382 insertions(+), 63 deletions(-)

diff --git a/controller/lflow.c b/controller/lflow.c
index e169edef1..83cbc5b87 100644
--- a/controller/lflow.c
+++ b/controller/lflow.c
@@ -1083,27 +1083,6 @@ lflow_handle_changed_ref(enum ref_type ref_type, const 
char *ref_name,
     return ret;
 }
 
-static void
-lflow_parse_ctrl_meter(const struct sbrec_logical_flow *lflow,
-                       struct ovn_extend_table *meter_table,
-                       uint32_t *meter_id)
-{
-    ovs_assert(meter_id);
-    *meter_id = NX_CTLR_NO_METER;
-
-    if (lflow->controller_meter) {
-        *meter_id = ovn_extend_table_assign_id(meter_table,
-                                               lflow->controller_meter,
-                                               lflow->header_.uuid);
-        if (*meter_id == EXT_TABLE_ID_INVALID) {
-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-            VLOG_WARN_RL(&rl, "Unable to assign id for meter: %s",
-                         lflow->controller_meter);
-            return;
-        }
-    }
-}
-
 static void
 add_matches_to_flow_table(const struct sbrec_logical_flow *lflow,
                           const struct local_datapath *ldp,
@@ -1126,8 +1105,10 @@ add_matches_to_flow_table(const struct 
sbrec_logical_flow *lflow,
      * controller.
      */
     uint32_t ctrl_meter_id = NX_CTLR_NO_METER;
-    lflow_parse_ctrl_meter(lflow, l_ctx_out->meter_table,
-                           &ctrl_meter_id);
+    if (lflow->controller_meter) {
+        ctrl_meter_id = ovn_controller_get_meter_id(l_ctx_in->meter_table,
+                                                    lflow->controller_meter);
+    }
 
     /* Encode OVN logical actions into OpenFlow. */
     uint64_t ofpacts_stub[1024 / 8];
@@ -1153,6 +1134,7 @@ add_matches_to_flow_table(const struct sbrec_logical_flow 
*lflow,
         .fdb_ptable = OFTABLE_GET_FDB,
         .fdb_lookup_ptable = OFTABLE_LOOKUP_FDB,
         .ctrl_meter_id = ctrl_meter_id,
+        .meter_hash = l_ctx_in->meter_table,
     };
     ovnacts_encode(ovnacts->data, ovnacts->size, &ep, &ofpacts);
 
diff --git a/controller/lflow.h b/controller/lflow.h
index d61733bc2..4760c5f62 100644
--- a/controller/lflow.h
+++ b/controller/lflow.h
@@ -153,6 +153,7 @@ struct lflow_ctx_in {
     const struct sset *active_tunnels;
     const struct sset *related_lport_ids;
     const struct hmap *chassis_tunnels;
+    const struct shash *meter_table;
 };
 
 struct lflow_ctx_out {
diff --git a/controller/ofctrl.c b/controller/ofctrl.c
index 19aa787f9..2f484c662 100644
--- a/controller/ofctrl.c
+++ b/controller/ofctrl.c
@@ -369,8 +369,6 @@ static enum mf_field_id mff_ovn_geneve;
  * is restarted, even if there is no change in the desired flow table. */
 static bool need_reinstall_flows;
 
-static ovs_be32 queue_msg(struct ofpbuf *);
-
 static struct ofpbuf *encode_flow_mod(struct ofputil_flow_mod *);
 
 static struct ofpbuf *encode_group_mod(const struct ofputil_group_mod *);
@@ -822,7 +820,7 @@ ofctrl_get_cur_cfg(void)
     return cur_cfg;
 }
 
-static ovs_be32
+ovs_be32
 queue_msg(struct ofpbuf *msg)
 {
     const struct ofp_header *oh = msg->data;
@@ -1978,29 +1976,16 @@ add_meter_string(struct ovn_extend_table_info 
*m_desired,
     free(meter_string);
 }
 
-static void
-add_meter(struct ovn_extend_table_info *m_desired,
-          const struct sbrec_meter_table *meter_table,
-          struct ovs_list *msgs)
+void
+meter_create_msg(struct ovs_list *msgs,
+                 const struct sbrec_meter *sb_meter,
+                 int cmd, int id)
 {
-    const struct sbrec_meter *sb_meter;
-    SBREC_METER_TABLE_FOR_EACH (sb_meter, meter_table) {
-        if (!strcmp(m_desired->name, sb_meter->name)) {
-            break;
-        }
-    }
-
-    if (!sb_meter) {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-        VLOG_ERR_RL(&rl, "could not find meter named \"%s\"", m_desired->name);
-        return;
-    }
-
     struct ofputil_meter_mod mm;
-    mm.command = OFPMC13_ADD;
-    mm.meter.meter_id = m_desired->table_id;
-    mm.meter.flags = OFPMF13_STATS;
+    mm.command = cmd;
+    mm.meter.meter_id = id;
 
+    mm.meter.flags = OFPMF13_STATS;
     if (!strcmp(sb_meter->unit, "pktps")) {
         mm.meter.flags |= OFPMF13_PKTPS;
     } else {
@@ -2329,7 +2314,6 @@ void
 ofctrl_put(struct ovn_desired_flow_table *lflow_table,
            struct ovn_desired_flow_table *pflow_table,
            struct shash *pending_ct_zones,
-           const struct sbrec_meter_table *meter_table,
            uint64_t req_cfg,
            bool lflows_changed,
            bool pflows_changed)
@@ -2414,8 +2398,6 @@ ofctrl_put(struct ovn_desired_flow_table *lflow_table,
             /* The "set-meter" action creates a meter entry name that
              * describes the meter itself. */
             add_meter_string(m_desired, &msgs);
-        } else {
-            add_meter(m_desired, meter_table, &msgs);
         }
     }
 
diff --git a/controller/ofctrl.h b/controller/ofctrl.h
index ad8f4be65..7c1844a72 100644
--- a/controller/ofctrl.h
+++ b/controller/ofctrl.h
@@ -29,6 +29,7 @@ struct match;
 struct ofpbuf;
 struct ovsrec_bridge;
 struct sbrec_meter_table;
+struct sbrec_meter;
 struct shash;
 
 struct ovn_desired_flow_table {
@@ -55,7 +56,6 @@ enum mf_field_id ofctrl_get_mf_field_id(void);
 void ofctrl_put(struct ovn_desired_flow_table *lflow_table,
                 struct ovn_desired_flow_table *pflow_table,
                 struct shash *pending_ct_zones,
-                const struct sbrec_meter_table *,
                 uint64_t nb_cfg,
                 bool lflow_changed,
                 bool pflow_changed);
@@ -145,5 +145,9 @@ void ofctrl_check_and_add_flow_metered(struct 
ovn_desired_flow_table *,
 bool ofctrl_is_connected(void);
 void ofctrl_set_probe_interval(int probe_interval);
 void ofctrl_get_memory_usage(struct simap *usage);
+ovs_be32 queue_msg(struct ofpbuf *msg);
+void meter_create_msg(struct ovs_list *msgs,
+                      const struct sbrec_meter *sb_meter,
+                      int cmd, int id);
 
 #endif /* controller/ofctrl.h */
diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index e4b8b1bdd..805f5dbf3 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -76,6 +76,8 @@
 #include "stopwatch.h"
 #include "lib/inc-proc-eng.h"
 #include "hmapx.h"
+#include "openvswitch/ofp-util.h"
+#include "openvswitch/ofp-meter.h"
 
 VLOG_DEFINE_THIS_MODULE(main);
 
@@ -968,7 +970,8 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl)
     SB_NODE(dhcpv6_options, "dhcpv6_options") \
     SB_NODE(dns, "dns") \
     SB_NODE(load_balancer, "load_balancer") \
-    SB_NODE(fdb, "fdb")
+    SB_NODE(fdb, "fdb") \
+    SB_NODE(meter, "meter")
 
 enum sb_engine_node {
 #define SB_NODE(NAME, NAME_STR) SB_##NAME,
@@ -1543,6 +1546,221 @@ addr_sets_sb_address_set_handler(struct engine_node 
*node, void *data)
     return true;
 }
 
+struct ed_type_meter {
+    unsigned long *ids;
+    struct shash meter_sets;
+};
+
+static void *
+en_meter_init(struct engine_node *node OVS_UNUSED,
+              struct engine_arg *arg OVS_UNUSED)
+{
+    struct ed_type_meter *m = xzalloc(sizeof *m);
+    m->ids = bitmap_allocate(MAX_METER_ID);
+    bitmap_set1(m->ids, 0); /* table id 0 is invalid. */
+    shash_init(&m->meter_sets);
+
+    return m;
+}
+
+static bool
+en_meter_data_check_bands(struct ed_meter_data *md,
+                          const struct sbrec_meter *sb_meter)
+{
+    if (md->n_bands != sb_meter->n_bands) {
+        return true;
+    }
+
+    for (int i = 0; i < sb_meter->n_bands; i++) {
+        int j;
+        for (j = 0; j < md->n_bands; j++) {
+            if (sb_meter->bands[i]->rate == md->bands[j].rate &&
+                sb_meter->bands[i]->burst_size == md->bands[j].burst_size) {
+                break;
+            }
+        }
+        if (j == md->n_bands) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static void
+en_meter_data_update(struct ed_meter_data *md,
+                     const struct sbrec_meter *sb_meter)
+{
+    free(md->bands);
+    md->n_bands = sb_meter->n_bands;
+    md->bands = xcalloc(md->n_bands, sizeof *md->bands);
+    for (int i = 0; i < sb_meter->n_bands; i++) {
+        md->bands[i].rate = sb_meter->bands[i]->rate;
+        md->bands[i].burst_size = sb_meter->bands[i]->burst_size;
+    }
+}
+
+static struct ed_meter_data *
+en_meter_data_alloc(struct ed_type_meter *m,
+                    const struct sbrec_meter *sb_meter)
+{
+    uint32_t id = bitmap_scan(m->ids, 0, 1, MAX_METER_ID + 1);
+    if (id == MAX_METER_ID + 1) {
+        static struct vlog_rate_limit rl =
+            VLOG_RATE_LIMIT_INIT(1, 1);
+        VLOG_ERR_RL(&rl, "%"PRIu32" out of meter ids.", id);
+        return NULL;
+    }
+
+    struct ed_meter_data *md = xzalloc(sizeof *md);
+    bitmap_set1(m->ids, id);
+    md->id = id;
+    en_meter_data_update(md, sb_meter);
+    shash_add(&m->meter_sets, sb_meter->name, md);
+
+    return md;
+}
+
+static void
+en_meter_data_destroy(struct ed_type_meter *m, struct ed_meter_data *md)
+{
+    bitmap_set0(m->ids, md->id);
+    free(md->bands);
+    free(md);
+}
+
+static void
+en_meter_send_msgs(struct ovs_list *msgs)
+{
+    if (ovs_list_is_empty(msgs)) {
+        return;
+    }
+
+    struct ofpbuf *barrier = ofputil_encode_barrier_request(OFP15_VERSION);
+    ovs_list_push_back(msgs, &barrier->list_node);
+    /* Queue the messages. */
+    struct ofpbuf *msg;
+    LIST_FOR_EACH_POP (msg, list_node, msgs) {
+        queue_msg(msg);
+    }
+}
+
+static void
+en_meter_delete_msg(struct ovs_list *msgs, uint32_t id)
+{
+    struct ofputil_meter_mod mm = {
+        .command = OFPMC13_DELETE,
+        .meter = { .meter_id = id },
+    };
+    struct ofpbuf *msg = ofputil_encode_meter_mod(OFP15_VERSION, &mm);
+    ovs_list_push_back(msgs, &msg->list_node);
+}
+
+static void
+en_meter_cleanup(void *data)
+{
+    struct ed_type_meter *m = data;
+
+    struct shash_node *node, *next;
+    SHASH_FOR_EACH_SAFE (node, next, &m->meter_sets) {
+        struct ed_meter_data *md = node->data;
+        shash_delete(&m->meter_sets, node);
+        en_meter_data_destroy(m, md);
+    }
+    shash_destroy(&m->meter_sets);
+    bitmap_free(m->ids);
+}
+
+static void
+en_meter_run(struct engine_node *node, void *data)
+{
+    struct ovs_list msgs = OVS_LIST_INITIALIZER(&msgs);
+    const struct sbrec_meter *sb_meter;
+    struct ed_type_meter *m = data;
+    struct ed_meter_data *md;
+
+    struct sbrec_meter_table *m_table =
+        (struct sbrec_meter_table *)EN_OVSDB_GET(
+            engine_get_input("SB_meter", node));
+
+    /* remove stale entries */
+    struct shash_node *iter, *next;
+    SHASH_FOR_EACH_SAFE (iter, next, &m->meter_sets) {
+        bool found = false;
+        SBREC_METER_TABLE_FOR_EACH (sb_meter, m_table) {
+            if (!strcmp(sb_meter->name, iter->name)) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            md = iter->data;
+            shash_delete(&m->meter_sets, iter);
+            en_meter_delete_msg(&msgs, md->id);
+            en_meter_data_destroy(m, md);
+        }
+    }
+
+    /* add new entries or update current ones */
+    SBREC_METER_TABLE_FOR_EACH (sb_meter, m_table) {
+        md = shash_find_data(&m->meter_sets, sb_meter->name);
+        if (md) {
+            if (en_meter_data_check_bands(md, sb_meter)) {
+                en_meter_data_update(md, sb_meter);
+                meter_create_msg(&msgs, sb_meter, OFPMC13_MODIFY, md->id);
+            }
+        } else {
+            md = en_meter_data_alloc(m, sb_meter);
+            if (md) {
+                meter_create_msg(&msgs, sb_meter, OFPMC13_ADD, md->id);
+            }
+        }
+    }
+
+    en_meter_send_msgs(&msgs);
+    engine_set_node_state(node, EN_UPDATED);
+}
+
+static bool
+meter_sb_meter_handler(struct engine_node *node, void *data)
+{
+    struct ovs_list msgs = OVS_LIST_INITIALIZER(&msgs);
+    struct ed_type_meter *m = data;
+    struct sbrec_meter_table *m_table =
+        (struct sbrec_meter_table *)EN_OVSDB_GET(
+            engine_get_input("SB_meter", node));
+
+    const struct sbrec_meter *iter;
+    SBREC_METER_TABLE_FOR_EACH_TRACKED (iter, m_table) {
+        struct ed_meter_data *md;
+        if (sbrec_meter_is_deleted(iter)) {
+            md = shash_find_and_delete(&m->meter_sets, iter->name);
+            if (md) {
+                en_meter_delete_msg(&msgs, md->id);
+                en_meter_data_destroy(m, md);
+                engine_set_node_state(node, EN_UPDATED);
+            }
+        } else {
+            if (sbrec_meter_is_new(iter)) {
+                md = en_meter_data_alloc(m, iter);
+                if (md) {
+                    meter_create_msg(&msgs, iter, OFPMC13_ADD, md->id);
+                    engine_set_node_state(node, EN_UPDATED);
+                }
+            } else {
+                md = shash_find_data(&m->meter_sets, iter->name);
+                if (md) {
+                    en_meter_data_update(md, iter);
+                }
+                meter_create_msg(&msgs, iter, OFPMC13_MODIFY, md->id);
+            }
+        }
+    }
+    en_meter_send_msgs(&msgs);
+
+    return true;
+}
+
 struct ed_type_port_groups{
     /* A copy of SB port_groups, each converted as a sset for efficient lport
      * lookup. */
@@ -2298,6 +2516,10 @@ init_lflow_ctx(struct engine_node *node,
         (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
             engine_get_input("OVS_open_vswitch", node));
 
+    struct ed_type_meter *meter_data =
+        engine_get_input_data("meter", node);
+    struct shash *meter_sets = &meter_data->meter_sets;
+
     const char *chassis_id = get_ovs_chassis_id(ovs_table);
     const struct sbrec_chassis *chassis = NULL;
     struct ovsdb_idl_index *sbrec_chassis_by_name =
@@ -2348,6 +2570,7 @@ init_lflow_ctx(struct engine_node *node,
     l_ctx_in->active_tunnels = &rt_data->active_tunnels;
     l_ctx_in->related_lport_ids = &rt_data->related_lports.lport_ids;
     l_ctx_in->chassis_tunnels = &non_vif_data->chassis_tunnels;
+    l_ctx_in->meter_table = meter_sets;
 
     l_ctx_out->flow_table = &fo->flow_table;
     l_ctx_out->group_table = &fo->group_table;
@@ -3222,6 +3445,7 @@ main(int argc, char *argv[])
     ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lflow_output, "logical_flow_output");
     ENGINE_NODE(flow_output, "flow_output");
     ENGINE_NODE_WITH_CLEAR_TRACK_DATA(addr_sets, "addr_sets");
+    ENGINE_NODE(meter, "meter");
     ENGINE_NODE_WITH_CLEAR_TRACK_DATA(port_groups, "port_groups");
 
 #define SB_NODE(NAME, NAME_STR) ENGINE_NODE_SB(NAME, NAME_STR);
@@ -3243,6 +3467,8 @@ main(int argc, char *argv[])
     engine_add_input(&en_port_groups, &en_runtime_data,
                      port_groups_runtime_data_handler);
 
+    engine_add_input(&en_meter, &en_sb_meter, meter_sb_meter_handler);
+
     engine_add_input(&en_non_vif_data, &en_ovs_open_vswitch, NULL);
     engine_add_input(&en_non_vif_data, &en_ovs_bridge, NULL);
     engine_add_input(&en_non_vif_data, &en_sb_chassis, NULL);
@@ -3287,6 +3513,7 @@ main(int argc, char *argv[])
                      lflow_output_runtime_data_handler);
     engine_add_input(&en_lflow_output, &en_non_vif_data,
                      NULL);
+    engine_add_input(&en_lflow_output, &en_meter, NULL);
 
     engine_add_input(&en_lflow_output, &en_sb_multicast_group,
                      lflow_output_sb_multicast_group_handler);
@@ -3768,7 +3995,6 @@ main(int argc, char *argv[])
                         ofctrl_put(&lflow_output_data->flow_table,
                                    &pflow_output_data->flow_table,
                                    &ct_zones_data->pending,
-                                   sbrec_meter_table_get(ovnsb_idl_loop.idl),
                                    ofctrl_seqno_get_req_cfg(),
                                    engine_node_changed(&en_lflow_output),
                                    engine_node_changed(&en_pflow_output));
diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index 0641b927e..bed24d9d1 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -799,6 +799,7 @@ struct ovnact_encode_params {
                                 * 'lookup_fdb' to resubmit. */
     uint32_t ctrl_meter_id;     /* Meter to be used if the resulting flow
                                    sends packets to controller. */
+    const struct shash *meter_hash;
 };
 
 void ovnacts_encode(const struct ovnact[], size_t ovnacts_len,
diff --git a/lib/actions.c b/lib/actions.c
index 5d3caaf2b..f7f5aae52 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -3411,10 +3411,9 @@ encode_LOG(const struct ovnact_log *log,
 {
     uint32_t meter_id = NX_CTLR_NO_METER;
 
-    if (log->meter) {
-        meter_id = ovn_extend_table_assign_id(ep->meter_table, log->meter,
-                                              ep->lflow_uuid);
-        if (meter_id == EXT_TABLE_ID_INVALID) {
+    if (ep->meter_hash && log->meter) {
+        meter_id = ovn_controller_get_meter_id(ep->meter_hash, log->meter);
+        if (meter_id == MAX_METER_ID + 1) {
             VLOG_WARN("Unable to assign id for log meter: %s", log->meter);
             return;
         }
diff --git a/lib/ovn-util.c b/lib/ovn-util.c
index a22ae84fe..b7e9d9b2c 100644
--- a/lib/ovn-util.c
+++ b/lib/ovn-util.c
@@ -30,6 +30,8 @@
 #include "socket-util.h"
 #include "svec.h"
 #include "unixctl.h"
+#include "controller/ovn-controller.h"
+#include "openvswitch/ofp-actions.h"
 
 VLOG_DEFINE_THIS_MODULE(ovn_util);
 
@@ -806,3 +808,10 @@ get_bridge(const struct ovsrec_bridge_table *bridge_table, 
const char *br_name)
     }
     return NULL;
 }
+
+uint32_t
+ovn_controller_get_meter_id(const struct shash *meter_sets, const char *name)
+{
+    struct ed_meter_data *md = shash_find_data(meter_sets, name);
+    return md ? md->id : NX_CTLR_NO_METER;
+}
diff --git a/lib/ovn-util.h b/lib/ovn-util.h
index 1fe91ba99..c5bb21d2d 100644
--- a/lib/ovn-util.h
+++ b/lib/ovn-util.h
@@ -303,5 +303,20 @@ struct ovsrec_bridge_table;
 const struct ovsrec_bridge *get_bridge(const struct ovsrec_bridge_table *,
                                        const char *br_name);
 
+#define MAX_METER_ID    (MAX_EXT_TABLE_ID / 2)
+struct ed_meter_band_data {
+    int64_t burst_size;
+    int64_t rate;
+};
+
+struct ed_meter_data {
+    uint32_t id; /* ovs meter id */
+    struct ed_meter_band_data *bands;
+    size_t n_bands;
+};
+
+struct shash;
+uint32_t ovn_controller_get_meter_id(const struct shash *meter_sets,
+                                     const char *name);
 
 #endif /* OVN_UTIL_H */
diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
index f5e5448a4..6e4c24f0f 100644
--- a/tests/ovn-controller.at
+++ b/tests/ovn-controller.at
@@ -725,7 +725,7 @@ check ovn-nbctl meter-add event-elb drop 100 pktps 10
 check ovn-nbctl --wait=hv copp-add copp0 event-elb event-elb
 check ovn-nbctl --wait=hv ls-copp-add copp0 ls1
 
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep controller | grep 
userdata=00.00.00.0f | grep -q meter_id=32768])
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep controller | grep 
userdata=00.00.00.0f | grep -q meter_id=1])
 
 check ovn-nbctl copp-del copp0
 AT_CHECK([ovn-nbctl copp-list copp0], [0], [dnl
@@ -738,13 +738,13 @@ check ovn-nbctl --wait=hv copp-add copp1 reject acl-meter
 check ovn-nbctl ls-copp-add copp1 ls1
 check ovn-nbctl --wait=hv acl-add ls1 from-lport 1002 'inport == "lsp1" && ip 
&& udp' reject
 
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep controller | grep 
userdata=00.00.00.16 | grep -q meter_id=32768])
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep controller | grep 
userdata=00.00.00.16 | grep -q meter_id=1])
 
 # arp metering
 check ovn-nbctl meter-add arp-meter drop 200 pktps 0
 check ovn-nbctl --wait=hv copp-add copp2 arp-resolve arp-meter
 check ovn-nbctl --wait=hv lr-copp-add copp2 lr1
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep controller | grep 
userdata=00.00.00.00 | grep -q meter_id=32769])
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep controller | grep 
userdata=00.00.00.00 | grep -q meter_id=2])
 
 OVN_CLEANUP([hv1])
 AT_CLEANUP
diff --git a/tests/ovn-performance.at b/tests/ovn-performance.at
index 10341ad72..aed1cb53a 100644
--- a/tests/ovn-performance.at
+++ b/tests/ovn-performance.at
@@ -543,6 +543,28 @@ OVN_CONTROLLER_EXPECT_HIT(
     [as hv3 ovs-vsctl set interface vgw3 external-ids:ovn-egress-iface=true]
 )
 
+ovn-nbctl --wait=hv meter-add meter0 drop 100 pktps 10
+
+OVN_CONTROLLER_EXPECT_NO_HIT(
+    [hv1 hv2 hv3 hv4], [lflow_run],
+    [ovn-nbctl --wait=hv copp-add copp0 arp meter0]
+)
+
+OVN_CONTROLLER_EXPECT_NO_HIT(
+    [hv1 hv2 hv3 hv4], [lflow_run],
+    [ovn-nbctl --wait=hv lr-copp-add copp0 lr1]
+)
+
+OVN_CONTROLLER_EXPECT_NO_HIT(
+    [hv1 hv2 hv3 hv4], [lflow_run],
+    [ovn-nbctl --wait=hv --may-exist meter-add meter0 drop 200 pktps 10]
+)
+
+OVN_CONTROLLER_EXPECT_HIT(
+    [hv1 hv2 hv3 hv4], [lflow_run],
+    [ovn-nbctl --wait=hv meter-del meter0]
+)
+
 for i in 1 2; do
     j=$((i%2 + 1))
     lp=lp$i
diff --git a/tests/ovn.at b/tests/ovn.at
index 7adf6003d..37bd4fce7 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -9158,9 +9158,10 @@ echo "Meter duration: $d_secs"
 AT_SKIP_IF([test $d_secs -gt 9])
 
 # Print some information that may help debugging.
-AT_CHECK([as hv ovs-appctl -t ovn-controller meter-table-list], [0], [dnl
-http-rl1: 32768
-http-rl2: 32769
+
+AT_CHECK([as hv ovs-ofctl dump-meters br-int -O OpenFlow13 | grep meter], [0], 
[dnl
+meter=1 pktps stats bands=
+meter=2 pktps stats bands=
 ])
 as hv ovs-ofctl -O OpenFlow13 meter-stats br-int
 
@@ -9223,9 +9224,12 @@ ovn-nbctl acl-del lsw0 to-lport 1000 'tcp.dst==80'
 ovn-nbctl --wait=hv sync
 AT_CHECK([ovs-ofctl -O OpenFlow13 dump-meters br-int | grep meter], [0], 
[ignore], [ignore])
 
-# Delete acl2, meter should be deleted in OVS
 ovn-nbctl acl-del lsw0 to-lport 1000 'tcp.dst==81'
 ovn-nbctl --wait=hv sync
+AT_CHECK([ovs-ofctl -O OpenFlow13 dump-meters br-int | grep meter], [0], 
[ignore], [ignore])
+
+ovn-nbctl meter-del http-rl1
+ovn-nbctl --wait=hv sync
 AT_CHECK([ovs-ofctl -O OpenFlow13 dump-meters br-int | grep meter], [1])
 
 OVN_CLEANUP([hv])
@@ -29647,3 +29651,60 @@ OVS_WAIT_UNTIL([
 OVN_CLEANUP([hv1],[hv2])
 AT_CLEANUP
 ])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn-controller - check meters update])
+AT_KEYWORDS([meters-update])
+
+ovn_start
+
+net_add n1
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.10
+
+check ovn-nbctl ls-add sw0
+check ovn-nbctl lsp-add sw0 lsp
+
+as hv1 ovs-vsctl \
+    -- add-port br-int vif1 \
+    -- set Interface vif1 external_ids:iface-id=lsp
+
+# Wait for port to be bound.
+wait_row_count Chassis 1 name=hv1
+ch=$(fetch_column Chassis _uuid name=hv1)
+wait_row_count Port_Binding 1 logical_port=lsp chassis=$ch
+
+# Add a new meter
+check ovn-nbctl meter-add meter0 drop 10 pktps
+check ovn-nbctl --log --severity=alert --meter=meter0 \
+                --name=http acl-add sw0 to-lport 1000 'tcp.dst == 80' drop
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([as hv1 ovs-ofctl -OOpenFlow15 dump-meters br-int | grep -q rate=10], 
[0])
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep -q meter_id=1], [0])
+
+# Update existing meter
+check ovn-nbctl --may-exist meter-add meter0 drop 20 pktps
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([as hv1 ovs-ofctl -OOpenFlow15 dump-meters br-int | grep -q rate=20], 
[0])
+
+# Add a new meter
+check ovn-nbctl meter-add meter1 drop 20 pktps
+check ovn-nbctl --log --severity=alert --meter=meter1 \
+                --name=dns acl-add sw0 to-lport 1000 'udp.dst == 53' drop
+check ovn-nbctl --wait=hv sync
+AT_CHECK([as hv1 ovs-ofctl -OOpenFlow15 dump-meters br-int | grep -q rate=20], 
[0])
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep -q meter_id=2], [0])
+
+# Remove meter0
+check ovn-nbctl meter-del meter0
+check ovn-nbctl --wait=hv sync
+AT_CHECK([as hv1 ovs-ofctl -OOpenFlow15 dump-meters br-int | grep -q rate=10], 
[1])
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep -q meter_id=1], [1])
+
+OVN_CLEANUP([hv1])
+AT_CLEANUP
+])
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
index 2dcd7e906..e6c317083 100644
--- a/tests/system-ovn.at
+++ b/tests/system-ovn.at
@@ -6661,8 +6661,25 @@ OVS_WAIT_UNTIL([
     test "${n_reject}" = "2"
 ])
 kill $(pidof tcpdump)
+rm -f reject.pcap
+
+# Let's update the meter
+NS_EXEC([sw01], [tcpdump -l -n -i sw01 icmp -Q in > reject.pcap &])
+check ovn-nbctl --may-exist meter-add acl-meter drop 10 pktps 0
+ip netns exec sw01 scapy -H <<-EOF
+p = IP(src="192.168.1.2", dst="192.168.1.1") / UDP(dport = 12345) / 
Raw(b"X"*64)
+send (p, iface='sw01', loop = 0, verbose = 0, count = 100)
+EOF
 
+# 10pps + 1 burst size
+OVS_WAIT_UNTIL([
+    n_reject=$(grep unreachable reject.pcap | wc -l)
+    test "${n_reject}" = "20"
+])
+
+kill $(pidof tcpdump)
 rm -f reject.pcap
+
 NS_EXEC([sw01], [tcpdump -l -n -i sw01 icmp -Q in > reject.pcap &])
 check ovn-nbctl --wait=hv copp-del copp0 reject
 
-- 
2.35.1

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to