Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

oper:tc-class: add support for bit-unit formatted leafs #182

Merged
merged 2 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 106 additions & 6 deletions src/lib/oper_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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" };
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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 !=
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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)) {
Expand All @@ -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);
}
}
Expand Down
15 changes: 9 additions & 6 deletions tests/run_startup_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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

Expand Down
7 changes: 7 additions & 0 deletions yang/iproute2-cmdgen-extensions.yang
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 10 additions & 8 deletions yang/iproute2-tc-qdisc.yang
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ module iproute2-tc-qdisc {
}
typedef bit-units {
type string{
pattern '\d+(\.\d+)?[tgmkTGMK]bit';
pattern '\d+(\.\d+)?([tgmkTGMK]bit|bit)';
}
}

Expand Down Expand Up @@ -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";
}
Expand Down Expand Up @@ -734,29 +736,29 @@ 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
bandwidth to spare. Defaults to the configured rate,
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.
Expand Down