From patchwork Fri Jan 26 08:34:39 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Guoshuai Li X-Patchwork-Id: 866204 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zSXK13hHrz9s83 for ; Fri, 26 Jan 2018 19:35:01 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id A448B1155; Fri, 26 Jan 2018 08:34:59 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 3BF001153 for ; Fri, 26 Jan 2018 08:34:58 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from smtp2203-239.mail.aliyun.com (smtp2203-239.mail.aliyun.com [121.197.203.239]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id AC66927C for ; Fri, 26 Jan 2018 08:34:55 +0000 (UTC) X-Alimail-AntiSpam: AC=CONTINUE; BC=0.07444706|-1; CH=green; FP=0|0|0|0|0|-1|-1|-1; HT=e01l07440; MF=ligs@dtdream.com; NM=1; PH=DS; RN=2; RT=2; SR=0; TI=SMTPD_---.AXYLHXl_1516955689; Received: from localhost.localdomain(mailfrom:ligs@dtdream.com fp:222.128.6.202) by smtp.aliyun-inc.com(10.147.42.22); Fri, 26 Jan 2018 16:34:50 +0800 From: Guoshuai Li To: ovs-dev@openvswitch.org Date: Fri, 26 Jan 2018 16:34:39 +0800 Message-Id: <20180126083439.8628-1-ligs@dtdream.com> X-Mailer: git-send-email 2.13.2.windows.1 X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH] ovn-nbctl: Add QoS commands. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org This patch provides the command line to add/delete/list QoS rule on the logical switch. Signed-off-by: Guoshuai Li --- 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 @@ +

Logical Switch QoS Rule Commands

+
+
[--may-exist] qos-add switch direction priority match rate burst dscp
+
+

+ Adds the specified QoS rule to switch. + direction must be either from-lport or + to-lport. priority must be between + 0 and 32767, inclusive. dscp + must be between 0 and 63, inclusive. + rate and burst must be between 1 + and 4294967295, inclusive. One of the rate or dscp + must be configured. A full description of the fields are in + ovn-nb(5). If --may-exist is specified, + adding a duplicated QoS rule succeeds but the QoS rule is not + really created. Without --may-exist, adding a + duplicated QoS rule results in error. +

+
+ +
qos-del switch [direction [priority match]]
+
+ Deletes QoS rules from switch. If only + switch is supplied, all the QoS rules from the logical + switch are deleted. If direction 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. +
+ +
qos-list switch
+
+ Lists the QoS rules on switch. +
+
+

Logical Switch Port Commands

[--may-exist] lsp-add switch port
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