From 658d0b564ab2c3a451ba267b101a54eab4731b46 Mon Sep 17 00:00:00 2001 From: Amjad Daraiseh Date: Fri, 30 Aug 2024 02:11:28 +0300 Subject: [PATCH 1/2] oper:tc-class: add support for bit-unit formatted leafs Signed-off-by: Amjad Daraiseh --- src/lib/oper_data.c | 112 +++++++++++++++++++++++++-- tests/run_startup_tests.sh | 15 ++-- yang/iproute2-cmdgen-extensions.yang | 7 ++ yang/iproute2-tc-qdisc.yang | 10 +-- 4 files changed, 127 insertions(+), 17 deletions(-) diff --git a/src/lib/oper_data.c b/src/lib/oper_data.c index 8e761bd..0fefc54 100644 --- a/src/lib/oper_data.c +++ b/src/lib/oper_data.c @@ -32,6 +32,7 @@ typedef enum { OPER_DEFAULT_VALUE_EXT, OPER_STOP_IF_EXT, OPER_COMBINE_VALUES_EXT, + OPER_CHANGE_VAL_FORMAT_EXT, OPER_SUB_JOBJ_EXT, OPER_DUMP_TC_FILTERS, OPER_DUMP_TC_CLASSES, @@ -47,6 +48,7 @@ char *oper_yang_ext_map[] = { [OPER_CMD_EXT] = "oper-cmd", [OPER_DEFAULT_VALUE_EXT] = "oper-default-val", [OPER_STOP_IF_EXT] = "oper-stop-if", [OPER_COMBINE_VALUES_EXT] = "oper-combine-values", + [OPER_CHANGE_VAL_FORMAT_EXT] = "oper-change-value-format", [OPER_SUB_JOBJ_EXT] = "oper-sub-jobj", [OPER_DUMP_TC_FILTERS] = "oper-dump-tc-filters", [OPER_DUMP_TC_CLASSES] = "oper-dump-tc-classes" }; @@ -131,6 +133,74 @@ int get_lys_extension(oper_extension_t ex_t, const struct lysc_node *s_node, cha return EXIT_FAILURE; } +/** + * Converts a given number in bytes to a formatted string representing the equivalent size in bits. + * The output follows the pattern: '\d+[tmkg]bit', where: + * - The number is converted from bytes to bits. + * - The appropriate unit (Tbit, Gbit, Mbit, Kbit, or bit) is selected based on the size. + * - If the conversion to a higher unit results in a decimal, the function drops to the next lower unit to display the exact value as an integer. + * + * @param [in] bytes: The input value in bytes to be converted. + * @return char*: A dynamically allocated string representing the formatted size in bits. + * The caller is responsible for freeing this memory. + */ +char *bytes_to_bit_units(uint64_t bytes) +{ + uint64_t bits = bytes * 8; // Convert bytes to bits + char *suffix; + double value; + if (bits >= 1e12) { + value = bits / 1e12; + if (value == (int)value) { + suffix = "Tbit"; + } else { + suffix = "Gbit"; + value = bits / 1e9; + } + } else if (bits >= 1e9) { + value = bits / 1e9; + if (value == (int)value) { + suffix = "Gbit"; + } else { + suffix = "Mbit"; + value = bits / 1e6; + } + } else if (bits >= 1e6) { + value = bits / 1e6; + if (value == (int)value) { + suffix = "Mbit"; + } else { + suffix = "Kbit"; + value = bits / 1e3; + } + } else if (bits >= 1e3) { + suffix = "Kbit"; + value = bits / 1e3; + } else { + suffix = "bit"; + value = bits; + } + char *result = malloc(50); + printf("bits1 %ld bytes %ld\n", bits, bytes); + sprintf(result, "%.0f%s", value, suffix); + return result; +} + +char *convert_value(struct json_object *cmd_out_jobj, struct json_object *convert_key_pair_jobj) +{ + json_object_object_foreach(convert_key_pair_jobj, key, val) + { + const char *src_format_str = key; + const char *dst_format_str = json_object_get_string(val); + + /* "byte" to "bit-units" conversion */ + if (strcmp(src_format_str, "byte") == 0 && strcmp(dst_format_str, "bit-units") == 0) { + uint64_t value = json_object_get_uint64(cmd_out_jobj); + return bytes_to_bit_units(value); + } + } +} + /** * Combine values from a JSON object based on keys and a separator from another JSON object. * @@ -627,8 +697,10 @@ void flags_to_leafs(struct json_object *temp_obj, struct json_object *fmap_jobj, void jdata_to_leaf(struct json_object *json_obj, const char *arg_name, struct lyd_node **parent_data_node, const struct lysc_node *s_node) { - char *vmap_str = NULL, *fmap_str = NULL, *combine_ext_str = NULL, *static_value = NULL; - struct json_object *fmap_jobj = NULL, *vmap_jobj = NULL, *combine_ext_jobj = NULL; + char *vmap_str = NULL, *fmap_str = NULL, *val_format_str = NULL, *combine_ext_str = NULL, + *static_value = NULL; + struct json_object *fmap_jobj = NULL, *vmap_jobj = NULL, *val_format_jobj = NULL, + *combine_ext_jobj = NULL; if (!strcmp(s_node->name, "netns")) { if (LY_SUCCESS != @@ -681,6 +753,24 @@ void jdata_to_leaf(struct json_object *json_obj, const char *arg_name, __func__, s_node->name); return; } + } else if (get_lys_extension(OPER_CHANGE_VAL_FORMAT_EXT, s_node, &val_format_str) == + EXIT_SUCCESS) { + if (val_format_str == NULL) { + fprintf(stderr, + "%s: ipr2cgen:oper-change-value-format extension found but failed to " + "get the conversion key pair \"%s\"\n", + __func__, s_node->name); + return; + } + val_format_jobj = val_format_str ? json_tokener_parse(val_format_str) : NULL; + free(val_format_str); + if (val_format_jobj == NULL) { + fprintf(stderr, + "%s: Error reading schema node \"%s\" ipr2cgen:oper-change-format extension," + " the extension value has a bad format\n", + __func__, s_node->name); + return; + } } else if (get_lys_extension(OPER_COMBINE_VALUES_EXT, s_node, &combine_ext_str) == EXIT_SUCCESS) { if (combine_ext_str == NULL) { @@ -725,7 +815,14 @@ void jdata_to_leaf(struct json_object *json_obj, const char *arg_name, flags_to_leafs(temp_obj, fmap_jobj, parent_data_node, s_node); } else { /* Process a single value */ - if (combine_ext_jobj != NULL) { + if (val_format_jobj) { + char *converted_value = convert_value(temp_obj, val_format_jobj); + if (LY_SUCCESS != lyd_new_term(*parent_data_node, NULL, s_node->name, + converted_value, 0, NULL)) { + fprintf(stderr, "%s: node %s creation failed\n", __func__, s_node->name); + } + free(converted_value); + } else if (combine_ext_jobj) { char *combined_value = combine_values(temp_obj, combine_ext_jobj); if (LY_SUCCESS != lyd_new_term(*parent_data_node, NULL, s_node->name, combined_value, 0, NULL)) { @@ -744,13 +841,16 @@ void jdata_to_leaf(struct json_object *json_obj, const char *arg_name, } } cleanup: - if (combine_ext_jobj != NULL) { + if (val_format_jobj) { + json_object_put(combine_ext_jobj); + } + if (combine_ext_jobj) { json_object_put(combine_ext_jobj); } - if (vmap_jobj != NULL) { + if (vmap_jobj) { json_object_put(vmap_jobj); } - if (fmap_jobj != NULL) { + if (fmap_jobj) { json_object_put(fmap_jobj); } } diff --git a/tests/run_startup_tests.sh b/tests/run_startup_tests.sh index a4ab106..1ab339a 100755 --- a/tests/run_startup_tests.sh +++ b/tests/run_startup_tests.sh @@ -205,9 +205,10 @@ tc filter add dev $qdisc_if9 egress pref 10 protocol ip flower dst_ip "13.13.13. # tc-class ip link add dev $qdisc_if10 type dummy tc qdisc add dev $qdisc_if10 root handle 1: htb -tc class add dev $qdisc_if10 parent root classid 1:10 htb rate 8Mbit prio 0 -tc class add dev $qdisc_if10 parent 1:10 classid 1:11 htb rate 8Mbit -tc class add dev $qdisc_if10 parent 1:11 classid 1:12 htb rate 8Mbit prio 7 burst 1000 cburst 10000 quantum 100 +tc class add dev $qdisc_if10 parent root classid 1:10 htb rate 1.1Gbit +tc class add dev $qdisc_if10 parent 1:10 classid 1:11 htb rate 8Mbit burst 1000 cburst 10000 +tc class add dev $qdisc_if10 parent 1:11 classid 1:12 htb rate 950Kbit +tc class add dev $qdisc_if10 parent 1:12 classid 1:13 htb rate 8bit prio 7 # Run iproute2-sysrepo and store its PID echo -e "\nSTARTING IPROUTE2-SYSREPO" @@ -678,11 +679,13 @@ check_dev_filter_rule $qdisc_if9 "ingress" "30" "src_ip" "12.12.12.12" "directio echo -e "\n" check_dev_filter_rule $qdisc_if9 "egress" "10" "dst_ip" "13.13.13.13" "gact" "pass" echo -e "\n" -check_tc_classes $qdisc_if10 "1:10" "parent" "root" +check_tc_classes $qdisc_if10 "1:10" "parent" "root" "rate" "1100Mbit" echo -e "\n" -check_tc_classes $qdisc_if10 "1:11" "parent" "1:10" +check_tc_classes $qdisc_if10 "1:11" "parent" "1:10" "rate" "8Mbit" "burst" "1000" "cburst" "10000" echo -e "\n" -check_tc_classes $qdisc_if10 "1:12" "parent" "1:11" "prio" "7" "burst" "1000" "cburst" "10000" "quantum" "100" +check_tc_classes $qdisc_if10 "1:12" "parent" "1:11" "rate" "950Kbit" +echo -e "\n" +check_tc_classes $qdisc_if10 "1:13" "parent" "1:12" "rate" "8bit" "prio" "7" echo -e "\n" cleanup diff --git a/yang/iproute2-cmdgen-extensions.yang b/yang/iproute2-cmdgen-extensions.yang index 2e02522..3afce54 100644 --- a/yang/iproute2-cmdgen-extensions.yang +++ b/yang/iproute2-cmdgen-extensions.yang @@ -66,6 +66,13 @@ module iproute2-cmdgen-extensions { argument "flag_map"; } + extension oper-change-value-format { + description "Instructs to convert iproute2 json output value from to a desired format. + it takes a json key pair where the key_name is the orignal format and the key value is the desired format. + example to convert bytes to packet rate use {\"byte\": \"bit-units\"}"; + argument "conversion_key"; + } + extension oper-combine-values { description "used to combine iproute2 operational json data keys in one leaf, comined json key names are expressed in json list format, the list key should be the combination diff --git a/yang/iproute2-tc-qdisc.yang b/yang/iproute2-tc-qdisc.yang index 10bcf84..7eb4af2 100644 --- a/yang/iproute2-tc-qdisc.yang +++ b/yang/iproute2-tc-qdisc.yang @@ -160,7 +160,7 @@ module iproute2-tc-qdisc { } typedef bit-units { type string{ - pattern '\d+(\.\d+)?[tgmkTGMK]bit'; + pattern '\d+(\.\d+)?([tgmkTGMK]bit|bit)'; } } @@ -734,14 +734,14 @@ module iproute2-tc-qdisc { "class priority"; } leaf rate { - ipr2cgen:oper-arg-name "not-supported"; + ipr2cgen:oper-change-value-format '{"byte": "bit-units"}'; type bit-units; description "Maximum rate this class and all its children are guaranteed. Mandatory. example 1kbit,3mbit etc "; } leaf ceil { - ipr2cgen:oper-arg-name "not-supported"; + ipr2cgen:oper-change-value-format '{"byte": "bit-units"}'; type bit-units; description "Maximum rate at which a class can send, if its parent has @@ -749,14 +749,14 @@ module iproute2-tc-qdisc { which implies no borrowing"; } leaf burst { - type uint32; + type uint64; description "Amount of bytes that can be burst at ceil speed, in excess of the configured rate. Should be at least as high as the highest burst of all children."; } leaf cburst { - type uint32; + type uint64; description " Amount of bytes that can be burst at 'infinite' speed, in other words, as fast as the interface can transmit them. From 4313f926ebbf9245b91ce682af54f2bcc6cfafcc Mon Sep 17 00:00:00 2001 From: Amjad Daraiseh Date: Fri, 30 Aug 2024 20:22:14 +0300 Subject: [PATCH 2/2] yang:qdisc: minor fixes for htb-options Signed-off-by: Amjad Daraiseh --- yang/iproute2-tc-qdisc.yang | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/yang/iproute2-tc-qdisc.yang b/yang/iproute2-tc-qdisc.yang index 7eb4af2..8279306 100644 --- a/yang/iproute2-tc-qdisc.yang +++ b/yang/iproute2-tc-qdisc.yang @@ -595,14 +595,16 @@ module iproute2-tc-qdisc { timescales. Because of further queues living in network adaptors, this is often not a problem."; leaf default { - ipr2cgen:oper-arg-name "not-supported"; - type uint32; + type string { + pattern '(0x)?[0-9a-fA-F]+|\d+'; + } description "Unclassified traffic gets sent to the class with this minor-id."; } - leaf offlaod { + leaf offload { ipr2cgen:flag; + ipr2cgen:oper-value-map '{"true":"on"}'; type enumeration { enum "on"; }