[ovs-dev] [PATCH] ovn-nbctl: Add QoS commands.

Guoshuai Li ligs at dtdream.com
Fri Jan 26 08:34:39 UTC 2018


This patch provides the command line to add/delete/list QoS rule on the
logical switch.

Signed-off-by: Guoshuai Li <ligs at dtdream.com>
---
 ovn/utilities/ovn-nbctl.8.xml |  36 +++++++
 ovn/utilities/ovn-nbctl.c     | 231 ++++++++++++++++++++++++++++++++++++++++++
 tests/ovn-nbctl.at            |  74 ++++++++++++++
 tests/ovn.at                  |   6 +-
 4 files changed, 346 insertions(+), 1 deletion(-)

diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml
index 3688d35b3..5613987f4 100644
--- a/ovn/utilities/ovn-nbctl.8.xml
+++ b/ovn/utilities/ovn-nbctl.8.xml
@@ -117,6 +117,42 @@
       </dd>
     </dl>
 
+    <h1>Logical Switch QoS Rule Commands</h1>
+    <dl>
+      <dt>[<code>--may-exist</code>] <code>qos-add</code> <var>switch</var> <var>direction</var> <var>priority</var> <var>match</var> <var>rate</var> <var>burst</var> <var>dscp</var></dt>
+      <dd>
+        <p>
+          Adds the specified QoS rule to <var>switch</var>.
+          <var>direction</var> must be either <code>from-lport</code> or
+          <code>to-lport</code>.  <var>priority</var> must be between
+          <code>0</code> and <code>32767</code>, inclusive. <var>dscp</var>
+          must be between <code>0</code> and <code>63</code>, inclusive.
+          <var>rate</var> and <var>burst</var> must be between <code>1</code>
+          and <code>4294967295</code>, inclusive. One of the rate or dscp
+          must be configured. A full description of the fields are in
+          <code>ovn-nb</code>(5). If <code>--may-exist</code> is specified,
+          adding a duplicated QoS rule succeeds but the QoS rule is not
+          really created. Without <code>--may-exist</code>, adding a
+          duplicated QoS rule results in error.
+        </p>
+      </dd>
+
+      <dt><code>qos-del</code> <var>switch</var> [<var>direction</var> [<var>priority</var> <var>match</var>]]</dt>
+      <dd>
+        Deletes QoS rules from <var>switch</var>.  If only
+        <var>switch</var> is supplied, all the QoS rules from the logical
+        switch are deleted.  If <var>direction</var> is also specified,
+        then all the flows in that direction will be deleted from the
+        logical switch.  If all the fields are given, then a single flow
+        that matches all the fields will be deleted.
+      </dd>
+
+      <dt><code>qos-list</code> <var>switch</var></dt>
+      <dd>
+        Lists the QoS rules on <var>switch</var>.
+      </dd>
+    </dl>
+
     <h1>Logical Switch Port Commands</h1>
     <dl>
       <dt>[<code>--may-exist</code>] <code>lsp-add</code> <var>switch</var> <var>port</var></dt>
diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
index c9aa2fef4..5fad4d17f 100644
--- a/ovn/utilities/ovn-nbctl.c
+++ b/ovn/utilities/ovn-nbctl.c
@@ -340,6 +340,13 @@ ACL commands:\n\
                             remove ACLs from SWITCH\n\
   acl-list SWITCH           print ACLs for SWITCH\n\
 \n\
+QoS commands:\n\
+  qos-add SWITCH DIRECTION PRIORITY MATCH [rate=RATE [burst=BURST]] [dscp=DSCP]\n\
+                            add an QoS rule to SWITCH\n\
+  qos-del SWITCH [DIRECTION [PRIORITY MATCH]]\n\
+                            remove QoS rules from SWITCH\n\
+  qos-list SWITCH           print QoS rules for SWITCH\n\
+\n\
 Logical switch port commands:\n\
   lsp-add SWITCH PORT       add logical port PORT on SWITCH\n\
   lsp-add SWITCH PORT PARENT TAG\n\
@@ -1388,6 +1395,26 @@ nbctl_acl_list(struct ctl_context *ctx)
     free(acls);
 }
 
+static int
+qos_cmp(const void *qos1_, const void *qos2_)
+{
+    const struct nbrec_qos *const *qos1p = qos1_;
+    const struct nbrec_qos *const *qos2p = qos2_;
+    const struct nbrec_qos *qos1 = *qos1p;
+    const struct nbrec_qos *qos2 = *qos2p;
+
+    int dir1 = dir_encode(qos1->direction);
+    int dir2 = dir_encode(qos2->direction);
+
+    if (dir1 != dir2) {
+        return dir1 < dir2 ? -1 : 1;
+    } else if (qos1->priority != qos2->priority) {
+        return qos1->priority > qos2->priority ? -1 : 1;
+    } else {
+        return strcmp(qos1->match, qos2->match);
+    }
+}
+
 static const char *
 parse_direction(const char *arg)
 {
@@ -1536,6 +1563,202 @@ nbctl_acl_del(struct ctl_context *ctx)
 }
 
 static void
+nbctl_qos_list(struct ctl_context *ctx)
+{
+    const struct nbrec_logical_switch *ls;
+    const struct nbrec_qos **qos_rules;
+    size_t i;
+
+    ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true);
+
+    qos_rules = xmalloc(sizeof *qos_rules * ls->n_qos_rules);
+    for (i = 0; i < ls->n_qos_rules; i++) {
+        qos_rules[i] = ls->qos_rules[i];
+    }
+
+    qsort(qos_rules, ls->n_qos_rules, sizeof *qos_rules, qos_cmp);
+
+    for (i = 0; i < ls->n_qos_rules; i++) {
+        const struct nbrec_qos *qos_rule = qos_rules[i];
+        ds_put_format(&ctx->output, "%10s %5"PRId64" (%s)",
+                      qos_rule->direction, qos_rule->priority,
+                      qos_rule->match);
+        for (size_t j = 0; j < qos_rule->n_bandwidth; j++) {
+            if (!strcmp(qos_rule->key_bandwidth[j], "rate")) {
+                ds_put_format(&ctx->output, " rate=%"PRId64"",
+                              qos_rule->value_bandwidth[j]);
+            }
+        }
+        for (size_t j = 0; j < qos_rule->n_bandwidth; j++) {
+            if (!strcmp(qos_rule->key_bandwidth[j], "burst")) {
+                ds_put_format(&ctx->output, " burst=%"PRId64"",
+                              qos_rule->value_bandwidth[j]);
+            }
+        }
+        for (size_t j = 0; j < qos_rule->n_action; j++) {
+            if (!strcmp(qos_rule->key_action[j], "dscp")) {
+                ds_put_format(&ctx->output, " dscp=%"PRId64"",
+                              qos_rule->value_action[j]);
+            }
+        }
+        ds_put_cstr(&ctx->output, "\n");
+    }
+
+    free(qos_rules);
+}
+
+static void
+nbctl_qos_add(struct ctl_context *ctx)
+{
+    const struct nbrec_logical_switch *ls
+        = ls_by_name_or_uuid(ctx, ctx->argv[1], true);
+    const char *direction = parse_direction(ctx->argv[2]);
+    int64_t priority = parse_priority(ctx->argv[3]);
+    int64_t dscp = -1;
+    int64_t rate = 0;
+    int64_t burst = 0;
+
+    for (int i = 5; i < ctx->argc; i++) {
+        if (!strncmp(ctx->argv[i], "dscp=", 5)) {
+            if (!ovs_scan(ctx->argv[i] + 5, "%"SCNd64, &dscp)
+                || dscp < 0 || dscp > 63) {
+                ctl_fatal("%s: dscp must in range 0...63.", ctx->argv[i] + 5);
+                return;
+            }
+        }
+        else if (!strncmp(ctx->argv[i], "rate=", 5)) {
+            if (!ovs_scan(ctx->argv[i] + 5, "%"SCNd64, &rate)
+                || rate < 1 || rate > 4294967295) {
+                ctl_fatal("%s: rate must in range 1...4294967295.",
+                          ctx->argv[i] + 5);
+                return;
+            }
+        }
+        else if (!strncmp(ctx->argv[i], "burst=", 6)) {
+            if (!ovs_scan(ctx->argv[i] + 6, "%"SCNd64, &burst)
+                || burst < 1 || burst > 4294967295) {
+                ctl_fatal("%s: burst must in range 1...4294967295.",
+                          ctx->argv[i] + 6);
+                return;
+            }
+        } else {
+            ctl_fatal("%s: must be start of \"dscp=\", \"rate=\", \"burst=\".",
+                      ctx->argv[i]);
+            return;
+        }
+    }
+
+    /* Validate rate and dscp. */
+    if (-1 == dscp && !rate) {
+        ctl_fatal("One of the rate or dscp must be configured.");
+        return;
+    }
+
+    /* Create the qos. */
+    struct nbrec_qos *qos = nbrec_qos_insert(ctx->txn);
+    nbrec_qos_set_priority(qos, priority);
+    nbrec_qos_set_direction(qos, direction);
+    nbrec_qos_set_match(qos, ctx->argv[4]);
+    if (-1 != dscp) {
+        const char *dscp_key = "dscp";
+        nbrec_qos_set_action(qos, &dscp_key, &dscp, 1);
+    }
+    if (rate) {
+        const char *bandwidth_key[2] = {"rate", "burst"};
+        const int64_t bandwidth_value[2] = {rate, burst};
+        size_t n_bandwidth = 1;
+        if (burst) {
+            n_bandwidth = 2;
+        }
+        nbrec_qos_set_bandwidth(qos, bandwidth_key, bandwidth_value,
+                                n_bandwidth);
+    }
+
+    /* Check if same qos rule already exists for the ls */
+    for (size_t i = 0; i < ls->n_qos_rules; i++) {
+        if (!qos_cmp(&ls->qos_rules[i], &qos)) {
+            bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
+            if (!may_exist) {
+                ctl_fatal("Same qos already existed on the ls %s.",
+                          ctx->argv[1]);
+            }
+            return;
+        }
+    }
+
+    /* Insert the qos rule the logical switch. */
+    nbrec_logical_switch_verify_qos_rules(ls);
+    struct nbrec_qos **new_qos_rules
+        = xmalloc(sizeof *new_qos_rules * (ls->n_qos_rules + 1));
+    nullable_memcpy(new_qos_rules,
+                    ls->qos_rules, sizeof *new_qos_rules * ls->n_qos_rules);
+    new_qos_rules[ls->n_qos_rules] = qos;
+    nbrec_logical_switch_set_qos_rules(ls, new_qos_rules,
+                                       ls->n_qos_rules + 1);
+    free(new_qos_rules);
+}
+
+static void
+nbctl_qos_del(struct ctl_context *ctx)
+{
+    const struct nbrec_logical_switch *ls
+        = ls_by_name_or_uuid(ctx, ctx->argv[1], true);
+
+    if (ctx->argc == 2) {
+        /* If direction, priority, and match are not specified, delete
+         * all QoS rules. */
+        nbrec_logical_switch_verify_qos_rules(ls);
+        nbrec_logical_switch_set_qos_rules(ls, NULL, 0);
+        return;
+    }
+
+    const char *direction = parse_direction(ctx->argv[2]);
+
+    /* If priority and match are not specified, delete all qos_rules with the
+     * specified direction. */
+    if (ctx->argc == 3) {
+        struct nbrec_qos **new_qos_rules
+            = xmalloc(sizeof *new_qos_rules * ls->n_qos_rules);
+
+        int n_qos_rules = 0;
+        for (size_t i = 0; i < ls->n_qos_rules; i++) {
+            if (strcmp(direction, ls->qos_rules[i]->direction)) {
+                new_qos_rules[n_qos_rules++] = ls->qos_rules[i];
+            }
+        }
+
+        nbrec_logical_switch_verify_qos_rules(ls);
+        nbrec_logical_switch_set_qos_rules(ls, new_qos_rules, n_qos_rules);
+        free(new_qos_rules);
+        return;
+    }
+
+    int64_t priority = parse_priority(ctx->argv[3]);
+
+    if (ctx->argc == 4) {
+        ctl_fatal("cannot specify priority without match");
+    }
+
+    /* Remove the matching rule. */
+    for (size_t i = 0; i < ls->n_qos_rules; i++) {
+        struct nbrec_qos *qos = ls->qos_rules[i];
+
+        if (priority == qos->priority && !strcmp(ctx->argv[4], qos->match) &&
+             !strcmp(direction, qos->direction)) {
+            struct nbrec_qos **new_qos_rules
+                = xmemdup(ls->qos_rules,
+                          sizeof *new_qos_rules * ls->n_qos_rules);
+            new_qos_rules[i] = ls->qos_rules[ls->n_qos_rules - 1];
+            nbrec_logical_switch_verify_qos_rules(ls);
+            nbrec_logical_switch_set_qos_rules(ls, new_qos_rules,
+                                          ls->n_qos_rules - 1);
+            free(new_qos_rules);
+            return;
+        }
+    }
+}
+
+static void
 nbctl_lb_add(struct ctl_context *ctx)
 {
     const char *lb_name = ctx->argv[1];
@@ -3723,6 +3946,14 @@ static const struct ctl_command_syntax nbctl_commands[] = {
       nbctl_acl_del, NULL, "", RW },
     { "acl-list", 1, 1, "SWITCH", NULL, nbctl_acl_list, NULL, "", RO },
 
+    /* qos commands. */
+    { "qos-add", 5, 7,
+      "SWITCH DIRECTION PRIORITY MATCH [rate=RATE [burst=BURST]] [dscp=DSCP]",
+      NULL, nbctl_qos_add, NULL, "--may-exist", RW },
+    { "qos-del", 1, 4, "SWITCH [DIRECTION [PRIORITY MATCH]]", NULL,
+      nbctl_qos_del, NULL, "", RW },
+    { "qos-list", 1, 1, "SWITCH", NULL, nbctl_qos_list, NULL, "", RO },
+
     /* logical switch port commands. */
     { "lsp-add", 2, 4, "SWITCH PORT [PARENT] [TAG]", NULL, nbctl_lsp_add,
       NULL, "--may-exist", RW },
diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at
index 5ac4a6d9b..4921417f3 100644
--- a/tests/ovn-nbctl.at
+++ b/tests/ovn-nbctl.at
@@ -230,6 +230,80 @@ OVN_NBCTL_TEST_STOP
 AT_CLEANUP
 
 dnl ---------------------------------------------------------------------
+
+AT_SETUP([ovn-nbctl - QoS])
+OVN_NBCTL_TEST_START
+
+AT_CHECK([ovn-nbctl ls-add ls0])
+AT_CHECK([ovn-nbctl qos-add ls0 from-lport 600 tcp dscp=63])
+AT_CHECK([ovn-nbctl qos-add ls0 from-lport 500 udp rate=100 burst=1000])
+AT_CHECK([ovn-nbctl qos-add ls0 from-lport 400 tcp dscp=0 rate=300 burst=3000])
+AT_CHECK([ovn-nbctl qos-add ls0 to-lport 300 tcp dscp=48])
+AT_CHECK([ovn-nbctl qos-add ls0 to-lport 200 ip rate=101])
+AT_CHECK([ovn-nbctl qos-add ls0 to-lport 100 ip4 dscp=13 rate=301 burst=30000])
+
+dnl Add duplicated qos
+AT_CHECK([ovn-nbctl qos-add ls0 to-lport 100 ip4 dscp=11 rate=302 burst=30002], [1], [], [stderr])
+AT_CHECK([grep 'already existed' stderr], [0], [ignore])
+AT_CHECK([ovn-nbctl --may-exist qos-add ls0 to-lport 100 ip4 dscp=11 rate=302 burst=30002])
+
+AT_CHECK([ovn-nbctl qos-list ls0], [0], [dnl
+from-lport   600 (tcp) dscp=63
+from-lport   500 (udp) rate=100 burst=1000
+from-lport   400 (tcp) rate=300 burst=3000 dscp=0
+  to-lport   300 (tcp) dscp=48
+  to-lport   200 (ip) rate=101
+  to-lport   100 (ip4) rate=301 burst=30000 dscp=13
+])
+
+dnl Delete in one direction.
+AT_CHECK([ovn-nbctl qos-del ls0 to-lport])
+AT_CHECK([ovn-nbctl qos-list ls0], [0], [dnl
+from-lport   600 (tcp) dscp=63
+from-lport   500 (udp) rate=100 burst=1000
+from-lport   400 (tcp) rate=300 burst=3000 dscp=0
+])
+
+dnl Delete all qos_rules.
+AT_CHECK([ovn-nbctl qos-del ls0])
+AT_CHECK([ovn-nbctl qos-list ls0], [0], [dnl
+])
+
+AT_CHECK([ovn-nbctl qos-add ls0 from-lport 600 ip rate=1000101])
+AT_CHECK([ovn-nbctl qos-add ls0 from-lport 400 tcp dscp=44])
+AT_CHECK([ovn-nbctl qos-add ls0 from-lport 200 ip burst=1000102 rate=301 dscp=19])
+
+dnl Delete a single flow.
+AT_CHECK([ovn-nbctl qos-del ls0 from-lport 400 tcp])
+AT_CHECK([ovn-nbctl qos-list ls0], [0], [dnl
+from-lport   600 (ip) rate=1000101
+from-lport   200 (ip) rate=301 burst=1000102 dscp=19
+])
+
+AT_CHECK([ovn-nbctl qos-add ls0 from-lport 600 ip rate=100010111111], [1], [],
+[ovn-nbctl: 100010111111: rate must in range 1...4294967295.
+])
+
+AT_CHECK([ovn-nbctl qos-add ls0 from-lport 600 ip burst=100010111112 rate=100010], [1], [],
+[ovn-nbctl: 100010111112: burst must in range 1...4294967295.
+])
+
+AT_CHECK([ovn-nbctl qos-add ls0 from-lport 600 ip dscp=-1], [1], [],
+[ovn-nbctl: -1: dscp must in range 0...63.
+])
+
+AT_CHECK([ovn-nbctl qos-add ls0 from-lport 600 ip dscpa=-1], [1], [],
+[ovn-nbctl: dscpa=-1: must be start of "dscp=", "rate=", "burst=".
+])
+
+AT_CHECK([ovn-nbctl qos-add ls0 from-lport 600 ip burst=123], [1], [],
+[ovn-nbctl: One of the rate or dscp must be configured.
+])
+
+OVN_NBCTL_TEST_STOP
+AT_CLEANUP
+
+dnl ---------------------------------------------------------------------
 AT_SETUP([ovn-nbctl - NATs])
 OVN_NBCTL_TEST_START
 AT_CHECK([ovn-nbctl lr-add lr0])
diff --git a/tests/ovn.at b/tests/ovn.at
index d02915e82..734dc6c1d 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -6033,6 +6033,8 @@ check_tos 0
 
 # Mark DSCP with a valid value
 qos_id=$(ovn-nbctl --wait=hv -- --id=@lp1-qos create QoS priority=100 action=dscp=48 match="inport\=\=\"lp1\"" direction="from-lport" -- set Logical_Switch lsw0 qos_rules=@lp1-qos)
+AT_CHECK([as hv ovn-nbctl qos-list lsw0 | wc -l], [0], [1
+])
 check_tos 48
 
 # check at hv without qos meter
@@ -6065,7 +6067,9 @@ ovn-nbctl --wait=hv set QoS $qos_id match="outport\=\=\"lp2\"" direction="to-lpo
 check_tos 63
 
 # Disable DSCP marking
-ovn-nbctl --wait=hv clear Logical_Switch lsw0 qos_rules
+ovn-nbctl --wait=hv qos-del lsw0
+AT_CHECK([as hv ovn-nbctl qos-list lsw0 | wc -l], [0], [0
+])
 check_tos 0
 
 # check at hv without qos meter
-- 
2.13.2.windows.1



More information about the dev mailing list