From 5281a9029ea56f397a37ea1cf478ac526882770a Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Tue, 23 Jul 2019 08:16:19 -0700 Subject: [PATCH] qos: QoS dump APIs Type: feature Change-Id: I514b40026986f3828c8727453456b20a0a45f3af Signed-off-by: Neale Ranns --- MAINTAINERS | 5 ++ src/vat/api_format.c | 4 +- src/vnet/qos/qos.api | 174 ++++++++++++++++++++++++++++++---------- src/vnet/qos/qos_api.c | 168 ++++++++++++++++++++++++++++++++++---- src/vnet/qos/qos_egress_map.c | 150 ++++++++++++++++++++-------------- src/vnet/qos/qos_egress_map.h | 9 +++ src/vnet/qos/qos_mark.c | 102 +++++++++++++++++++++++ src/vnet/qos/qos_mark.h | 6 ++ src/vnet/qos/qos_record.c | 102 ++++++++++++++++++++++- src/vnet/qos/qos_record.h | 5 ++ src/vpp/api/custom_dump.c | 4 +- test/test_qos.py | 182 ++++++++++++++---------------------------- test/vpp_papi_provider.py | 22 ----- test/vpp_qos.py | 110 +++++++++++++++++++++++++ 14 files changed, 774 insertions(+), 269 deletions(-) create mode 100644 test/vpp_qos.py diff --git a/MAINTAINERS b/MAINTAINERS index 9c9af5d9584..40d1383b2cd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -116,6 +116,11 @@ I: ip M: Dave Barach F: src/vnet/ip/ +VNET QoS +I: qos +M: Neale Ranns +F: src/vnet/qos/ + VNET Interface Common I: interface M: Dave Barach diff --git a/src/vat/api_format.c b/src/vat/api_format.c index d2313106fc6..69a57ab3d33 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -21373,8 +21373,8 @@ api_qos_record_enable_disable (vat_main_t * vam) M (QOS_RECORD_ENABLE_DISABLE, mp); - mp->sw_if_index = ntohl (sw_if_index); - mp->input_source = qs; + mp->record.sw_if_index = ntohl (sw_if_index); + mp->record.input_source = qs; mp->enable = enable; S (mp); diff --git a/src/vnet/qos/qos.api b/src/vnet/qos/qos.api index 1960ccb9548..11c91638c5b 100644 --- a/src/vnet/qos/qos.api +++ b/src/vnet/qos/qos.api @@ -22,7 +22,7 @@ option version = "1.0.0"; -enum qos_source +enum qos_source : u8 { QOS_API_SOURCE_EXT = 0, QOS_API_SOURCE_VLAN = 1, @@ -30,76 +30,164 @@ enum qos_source QOS_API_SOURCE_IP = 3, }; -/** \brief Enable/Disable QoS recording - The QoS bits from the packet at the specified input layer are copied - into the packet. Recording should be used in conjunction with marking - @param sw_if_index - The interface on which recording is enabled. - @param enable - enable=1 or disable the feature - @param input_source - The input source/layer at which the QoS bits - are copied from the packet. See qos_source_t. -*/ +/** + * QoS recording. + * @param sw_if_index - The interface on which recording is enabled. + * @param input_source - The input source/layer at which the QoS bits + are copied from the packet. See qos_source_t. + */ +typedef qos_record +{ + u32 sw_if_index; + vl_api_qos_source_t input_source; +}; + +/** + * Enable/Disable QoS recording + * The QoS bits from the packet at the specified input layer are copied + * into the packet. Recording should be used in conjunction with marking + * @param enable - enable=1 or disable the feature + * @param record - Recording configuration + */ autoreply define qos_record_enable_disable { u32 client_index; u32 context; - u32 sw_if_index; - vl_api_qos_source_t input_source; u8 enable; + vl_api_qos_record_t record; }; -/** \brief A row within a QoS map - Each value translates from an input value to an output. -*/ -typeonly define qos_egress_map_row +/** + * Dump the QoS record configs + */ +define qos_record_dump { - u8 outputs[256]; + u32 client_index; + u32 context; }; -/** \brief Update a QoS Map - A QoS map, translates from the QoS value in the packet set by the 'record' - feature, to the value used for output in the 'mark' feature. - There is one row in the map for each input/record source. - The MAP is then applied to the egress interface at for a given output source - @param map_id - client provided identifier for the map - @param rows - one row (per-input source) of output values -*/ +/** + * Details of QoS recording configs + */ +define qos_record_details +{ + u32 context; + vl_api_qos_record_t record; +}; + +/** + * @brief A row within a QoS map + * Each value translates from an input value to an output. + */ +typedef qos_egress_map_row +{ + u8 outputs[256]; +}; + +/** + * QoS Translation Map + * + * @param id - client provided identifier for the map + * @param rows - one row (per-input source) of output values + */ +typedef qos_egress_map +{ + u32 id; + vl_api_qos_egress_map_row_t rows[4]; +}; + +/** + * @brief Update a QoS Map + * A QoS map, translates from the QoS value in the packet set by the 'record' + * feature, to the value used for output in the 'mark' feature. + * There is one row in the map for each input/record source. + * The MAP is then applied to the egress interface at for a given output source + * @param map - The Map + */ autoreply define qos_egress_map_update { u32 client_index; u32 context; - u32 map_id; - vl_api_qos_egress_map_row_t rows[4]; + vl_api_qos_egress_map_t map; }; -/** \brief Delete a Qos Map - @param map_id - ID of the map to delete -*/ +/** + * @brief Delete a Qos Map + * @param map_id - ID of the map to delete + */ autoreply define qos_egress_map_delete { u32 client_index; u32 context; - u32 map_id; + u32 id; }; -/** \brief Enable/Disable QoS marking - The QoS bits from the packet are mapped (using the desired egress map) - into the header of the 'output-source'. Marking should be used in - conjunction with recording - @param sw_if_index - The interface on which recording is enabled. - @param enable - enable=1 or disable the feature - @param output_source - The output source/layer at which the QoS bits - are written into the packet. See qos_source_t. - @param map_id - The ID of the MAP in which the translation from input - to output is performed. -*/ -autoreply define qos_mark_enable_disable +/** + * Dump the QoS egress maps + */ +define qos_egress_map_dump { u32 client_index; u32 context; - u32 map_id; +}; + +/** + * QoS map details + */ +define qos_egress_map_details +{ + u32 context; + vl_api_qos_egress_map_t map; +}; + +/** + * QoS marking Cponfiguration + * The QoS bits from the buffer are mapped (using the desired egress map) + * into the header of the 'output-source'. Marking should be used in + * conjunction with recording + * @param sw_if_index - The interface on which recording is enabled. + * @param output_source - The output source/layer at which the QoS bits + * are written into the packet. See qos_source_t. + * @param map_id - The ID of the MAP in which the translation from input + * to output is performed. + */ +typedef qos_mark +{ u32 sw_if_index; + u32 map_id; vl_api_qos_source_t output_source; +}; + +/** + * @brief Enable/Disable QoS marking + * @param enable - enable=1 or disable the feature + * @param mark - Marking config + */ +autoreply define qos_mark_enable_disable +{ + u32 client_index; + u32 context; u8 enable; + vl_api_qos_mark_t mark; +}; + +/** + * Dump QoS marking configs + */ +define qos_mark_dump +{ + u32 client_index; + u32 context; + u32 sw_if_index; +}; + +/** + * QoS marking details + */ +autoreply define qos_mark_details +{ + u32 context; + vl_api_qos_mark_t mark; }; /* diff --git a/src/vnet/qos/qos_api.c b/src/vnet/qos/qos_api.c index 6297c24a843..966ffcce395 100644 --- a/src/vnet/qos/qos_api.c +++ b/src/vnet/qos/qos_api.c @@ -44,15 +44,16 @@ #define foreach_qos_api_msg \ _(QOS_RECORD_ENABLE_DISABLE, qos_record_enable_disable) \ + _(QOS_RECORD_DUMP, qos_record_dump) \ _(QOS_EGRESS_MAP_DELETE, qos_egress_map_delete) \ _(QOS_EGRESS_MAP_UPDATE, qos_egress_map_update) \ - _(QOS_MARK_ENABLE_DISABLE, qos_mark_enable_disable) + _(QOS_EGRESS_MAP_DUMP, qos_egress_map_dump) \ + _(QOS_MARK_ENABLE_DISABLE, qos_mark_enable_disable) \ + _(QOS_MARK_DUMP, qos_mark_dump) static int qos_source_decode (vl_api_qos_source_t v, qos_source_t * q) { - v = ntohl (v); - switch (v) { case QOS_API_SOURCE_EXT: @@ -72,6 +73,12 @@ qos_source_decode (vl_api_qos_source_t v, qos_source_t * q) return (VNET_API_ERROR_INVALID_VALUE); } +static vl_api_qos_source_t +qos_source_encode (qos_source_t q) +{ + return ((vl_api_qos_source_t) q); +} + void vl_api_qos_record_enable_disable_t_handler (vl_api_qos_record_enable_disable_t * mp) @@ -80,19 +87,63 @@ vl_api_qos_record_enable_disable_t_handler (vl_api_qos_record_enable_disable_t qos_source_t qs; int rv = 0; - rv = qos_source_decode (mp->input_source, &qs); + VALIDATE_SW_IF_INDEX (&(mp->record)); + + rv = qos_source_decode (mp->record.input_source, &qs); if (0 == rv) { if (mp->enable) - rv = qos_record_enable (ntohl (mp->sw_if_index), qs); + rv = qos_record_enable (ntohl (mp->record.sw_if_index), qs); else - rv = qos_record_disable (ntohl (mp->sw_if_index), qs); + rv = qos_record_disable (ntohl (mp->record.sw_if_index), qs); } + BAD_SW_IF_INDEX_LABEL; REPLY_MACRO (VL_API_QOS_RECORD_ENABLE_DISABLE_REPLY); } +typedef struct qos_record_send_walk_ctx_t_ +{ + vl_api_registration_t *reg; + u32 context; +} qos_record_send_walk_ctx_t; + +static walk_rc_t +send_qos_record_details (u32 sw_if_index, qos_source_t input_source, void *c) +{ + qos_record_send_walk_ctx_t *ctx; + vl_api_qos_record_details_t *mp; + + ctx = c; + mp = vl_msg_api_alloc_zero (sizeof (*mp)); + + mp->_vl_msg_id = ntohs (VL_API_QOS_RECORD_DETAILS); + mp->context = ctx->context; + mp->record.sw_if_index = htonl (sw_if_index); + mp->record.input_source = qos_source_encode (input_source); + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (WALK_CONTINUE); +} + +static void +vl_api_qos_record_dump_t_handler (vl_api_qos_record_dump_t * mp) +{ + vl_api_registration_t *reg; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + qos_record_send_walk_ctx_t ctx = { + .reg = reg, + .context = mp->context, + }; + qos_record_walk (send_qos_record_details, &ctx); +} + void vl_api_qos_egress_map_update_t_handler (vl_api_qos_egress_map_update_t * mp) { @@ -102,7 +153,8 @@ vl_api_qos_egress_map_update_t_handler (vl_api_qos_egress_map_update_t * mp) FOR_EACH_QOS_SOURCE (qs) { - qos_egress_map_update (ntohl (mp->map_id), qs, &mp->rows[qs].outputs[0]); + qos_egress_map_update (ntohl (mp->map.id), qs, + &mp->map.rows[qs].outputs[0]); } REPLY_MACRO (VL_API_QOS_EGRESS_MAP_UPDATE_REPLY); @@ -114,33 +166,121 @@ vl_api_qos_egress_map_delete_t_handler (vl_api_qos_egress_map_delete_t * mp) vl_api_qos_egress_map_delete_reply_t *rmp; int rv = 0; - qos_egress_map_delete (ntohl (mp->map_id)); + qos_egress_map_delete (ntohl (mp->id)); REPLY_MACRO (VL_API_QOS_EGRESS_MAP_DELETE_REPLY); } +typedef struct qos_egress_map_send_walk_ctx_t_ +{ + vl_api_registration_t *reg; + u32 context; +} qos_egress_map_send_walk_ctx_t; + +static walk_rc_t +send_qos_egress_map_details (qos_egress_map_id_t id, + const qos_egress_map_t * m, void *c) +{ + qos_egress_map_send_walk_ctx_t *ctx; + vl_api_qos_egress_map_details_t *mp; + u8 ii; + + ctx = c; + mp = vl_msg_api_alloc_zero (sizeof (*mp)); + + mp->_vl_msg_id = ntohs (VL_API_QOS_EGRESS_MAP_DETAILS); + mp->context = ctx->context; + mp->map.id = htonl (id); + + for (ii = 0; ii < 4; ii++) + clib_memcpy (mp->map.rows[ii].outputs, m->qem_output[ii], 256); + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (WALK_CONTINUE); +} + +static void +vl_api_qos_egress_map_dump_t_handler (vl_api_qos_egress_map_dump_t * mp) +{ + vl_api_registration_t *reg; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + qos_egress_map_send_walk_ctx_t ctx = { + .reg = reg, + .context = mp->context, + }; + qos_egress_map_walk (send_qos_egress_map_details, &ctx); +} + void - vl_api_qos_mark_enable_disable_t_handler - (vl_api_qos_mark_enable_disable_t * mp) +vl_api_qos_mark_enable_disable_t_handler (vl_api_qos_mark_enable_disable_t * + mp) { vl_api_qos_mark_enable_disable_reply_t *rmp; qos_source_t qs; int rv = 0; - rv = qos_source_decode (mp->output_source, &qs); + rv = qos_source_decode (mp->mark.output_source, &qs); if (0 == rv) { if (mp->enable) - rv = - qos_mark_enable (ntohl (mp->sw_if_index), qs, ntohl (mp->map_id)); + rv = qos_mark_enable (ntohl (mp->mark.sw_if_index), + qs, ntohl (mp->mark.map_id)); else - rv = qos_mark_disable (ntohl (mp->sw_if_index), qs); + rv = qos_mark_disable (ntohl (mp->mark.sw_if_index), qs); } REPLY_MACRO (VL_API_QOS_MARK_ENABLE_DISABLE_REPLY); } +typedef struct qos_mark_send_walk_ctx_t_ +{ + vl_api_registration_t *reg; + u32 context; +} qos_mark_send_walk_ctx_t; + +static walk_rc_t +send_qos_mark_details (u32 sw_if_index, + u32 map_id, qos_source_t output_source, void *c) +{ + qos_mark_send_walk_ctx_t *ctx; + vl_api_qos_mark_details_t *mp; + + ctx = c; + mp = vl_msg_api_alloc_zero (sizeof (*mp)); + + mp->_vl_msg_id = ntohs (VL_API_QOS_MARK_DETAILS); + mp->context = ctx->context; + mp->mark.sw_if_index = htonl (sw_if_index); + mp->mark.output_source = qos_source_encode (output_source); + mp->mark.map_id = htonl (map_id); + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (WALK_CONTINUE); +} + +static void +vl_api_qos_mark_dump_t_handler (vl_api_qos_mark_dump_t * mp) +{ + vl_api_registration_t *reg; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + qos_mark_send_walk_ctx_t ctx = { + .reg = reg, + .context = mp->context, + }; + qos_mark_walk (send_qos_mark_details, &ctx); +} + #define vl_msg_name_crc_list #include #undef vl_msg_name_crc_list diff --git a/src/vnet/qos/qos_egress_map.c b/src/vnet/qos/qos_egress_map.c index 158ff8445c6..7985579d3cf 100644 --- a/src/vnet/qos/qos_egress_map.c +++ b/src/vnet/qos/qos_egress_map.c @@ -41,6 +41,23 @@ qos_egress_map_find (qos_egress_map_id_t mid) return (INDEX_INVALID); } +qos_egress_map_id_t +qos_egress_map_get_id (index_t qemi) +{ + qos_egress_map_id_t qid; + index_t qmi; + + /* *INDENT-OFF* */ + hash_foreach(qid, qmi, qem_db, + ({ + if (qmi == qemi) + return (qid); + })); + /* *INDENT-OFF* */ + + return (~0); +} + qos_egress_map_t * qos_egress_map_find_i (qos_egress_map_id_t mid) { @@ -106,6 +123,20 @@ qos_egress_map_delete (qos_egress_map_id_t mid) } } +void +qos_egress_map_walk (qos_egress_map_walk_cb_t fn, void *c) +{ + qos_egress_map_id_t qid; + index_t qmi; + + /* *INDENT-OFF* */ + hash_foreach(qid, qmi, qem_db, + ({ + fn(qid, pool_elt_at_index(qem_pool, qmi), c); + })); + /* *INDENT-OFF* */ +} + static clib_error_t * qos_egress_map_update_cli (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) @@ -159,55 +190,54 @@ VLIB_CLI_COMMAND (qos_egress_map_update_command, static) = { }; /* *INDENT-ON* */ -u8 * -format_qos_egress_map (u8 * s, va_list * args) -{ - qos_egress_map_t *qem = va_arg (*args, qos_egress_map_t *); - u32 indent = va_arg (*args, u32); - int qs; - u32 ii; - - FOR_EACH_QOS_SOURCE (qs) + u8 *format_qos_egress_map (u8 * s, va_list * args) { - s = format (s, "%U%U:[", - format_white_space, indent, format_qos_source, qs); - - for (ii = 0; ii < ARRAY_LEN (qem->qem_output[qs]) - 1; ii++) - { - s = format (s, "%d,", qem->qem_output[qs][ii]); - } - s = format (s, "%d]\n", qem->qem_output[qs][ii]); - } + qos_egress_map_t *qem = va_arg (*args, qos_egress_map_t *); + u32 indent = va_arg (*args, u32); + int qs; + u32 ii; - return (s); -} - -static clib_error_t * -qos_egress_map_show (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - qos_egress_map_id_t map_id; - qos_egress_map_t *qem; - clib_error_t *error; - - map_id = ~0; - qem = NULL; - error = NULL; - - while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + FOR_EACH_QOS_SOURCE (qs) { - if (unformat (input, "id %d", &map_id)) - ; - else + s = format (s, "%U%U:[", + format_white_space, indent, format_qos_source, qs); + + for (ii = 0; ii < ARRAY_LEN (qem->qem_output[qs]) - 1; ii++) { - error = unformat_parse_error (input); - goto done; + s = format (s, "%d,", qem->qem_output[qs][ii]); } + s = format (s, "%d]\n", qem->qem_output[qs][ii]); } - if (~0 == map_id) - { - index_t qemi; + return (s); + } + + static clib_error_t *qos_egress_map_show (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) + { + qos_egress_map_id_t map_id; + qos_egress_map_t *qem; + clib_error_t *error; + + map_id = ~0; + qem = NULL; + error = NULL; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "id %d", &map_id)) + ; + else + { + error = unformat_parse_error (input); + goto done; + } + } + + if (~0 == map_id) + { + index_t qemi; /* *INDENT-OFF* */ hash_foreach(map_id, qemi, qem_db, @@ -218,25 +248,25 @@ qos_egress_map_show (vlib_main_t * vm, pool_elt_at_index(qem_pool, qemi), 2); })); /* *INDENT-ON* */ - } - else - { - qem = qos_egress_map_find_i (map_id); - - if (NULL == qem) - { - error = clib_error_return (0, "No Map for ID %d", map_id); - } - else - { - vlib_cli_output (vm, " Map-ID:%d\n%U", - map_id, format_qos_egress_map, qem, 2); - } - } + } + else + { + qem = qos_egress_map_find_i (map_id); + + if (NULL == qem) + { + error = clib_error_return (0, "No Map for ID %d", map_id); + } + else + { + vlib_cli_output (vm, " Map-ID:%d\n%U", + map_id, format_qos_egress_map, qem, 2); + } + } -done: - return (error); -} + done: + return (error); + } /*? * Show Egress Qos Maps diff --git a/src/vnet/qos/qos_egress_map.h b/src/vnet/qos/qos_egress_map.h index 3b6e0b45a43..0dab7adc2dd 100644 --- a/src/vnet/qos/qos_egress_map.h +++ b/src/vnet/qos/qos_egress_map.h @@ -66,6 +66,15 @@ extern void qos_egress_map_delete (qos_egress_map_id_t tid); * Get the VPP QoS map index from the user's map-ID */ extern index_t qos_egress_map_find (qos_egress_map_id_t tid); +extern qos_egress_map_id_t qos_egress_map_get_id (index_t qemi); + +/** + * Walk each of the configured maps + */ +typedef walk_rc_t (*qos_egress_map_walk_cb_t) (qos_egress_map_id_t id, + const qos_egress_map_t * m, + void *c); +void qos_egress_map_walk (qos_egress_map_walk_cb_t fn, void *c); /** * Data-plane functions diff --git a/src/vnet/qos/qos_mark.c b/src/vnet/qos/qos_mark.c index dcb0f9d1ab3..44bb34bd010 100644 --- a/src/vnet/qos/qos_mark.c +++ b/src/vnet/qos/qos_mark.c @@ -117,6 +117,24 @@ qos_mark_disable (u32 sw_if_index, qos_source_t output_source) return (0); } +void +qos_mark_walk (qos_mark_walk_cb_t fn, void *c) +{ + qos_source_t qs; + + FOR_EACH_QOS_SOURCE (qs) + { + u32 sw_if_index; + + vec_foreach_index (sw_if_index, qos_mark_configs[qs]) + { + if (INDEX_INVALID != qos_mark_configs[qs][sw_if_index]) + fn (sw_if_index, + qos_egress_map_get_id (qos_mark_configs[qs][sw_if_index]), qs, c); + } + } +} + static clib_error_t * qos_mark_cli (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) @@ -178,6 +196,90 @@ VLIB_CLI_COMMAND (qos_egress_map_interface_command, static) = { }; /* *INDENT-ON* */ +static void +qos_mark_show_one_interface (vlib_main_t * vm, u32 sw_if_index) +{ + index_t qemis[QOS_N_SOURCES]; + qos_source_t qs; + bool set; + + set = false; + clib_memset_u32 (qemis, INDEX_INVALID, QOS_N_SOURCES); + + FOR_EACH_QOS_SOURCE (qs) + { + if (vec_len (qos_mark_configs[qs]) <= sw_if_index) + continue; + if (INDEX_INVALID != (qemis[qs] = qos_mark_configs[qs][sw_if_index])) + set = true; + } + + if (set) + { + vlib_cli_output (vm, " %U:", format_vnet_sw_if_index_name, + vnet_get_main (), sw_if_index); + + FOR_EACH_QOS_SOURCE (qs) + { + if (qemis[qs] != INDEX_INVALID) + vlib_cli_output (vm, " %U: map:%d", format_qos_source, qs, + qemis[qs]); + } + } +} + +static clib_error_t * +qos_mark_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vnet_main_t *vnm = vnet_get_main (); + qos_source_t qs; + u32 sw_if_index; + + sw_if_index = ~0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%U", unformat_vnet_sw_interface, + vnm, &sw_if_index)) + ; + } + + if (~0 == sw_if_index) + { + u32 ii, n_ints = 0; + + FOR_EACH_QOS_SOURCE (qs) + { + n_ints = clib_max (n_ints, vec_len (qos_mark_configs[qs])); + } + + for (ii = 0; ii < n_ints; ii++) + { + qos_mark_show_one_interface (vm, ii); + } + } + else + qos_mark_show_one_interface (vm, sw_if_index); + + return (NULL); +} + +/*? + * Show Egress Qos Maps + * + * @cliexpar + * @cliexcmd{show qos egress map} + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (qos_mark_show_command, static) = { + .path = "show qos mark", + .short_help = "show qos mark [interface]", + .function = qos_mark_show, + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vnet/qos/qos_mark.h b/src/vnet/qos/qos_mark.h index f1705d1bb33..91e2024af94 100644 --- a/src/vnet/qos/qos_mark.h +++ b/src/vnet/qos/qos_mark.h @@ -28,6 +28,12 @@ extern int qos_mark_enable (u32 sw_if_index, qos_egress_map_id_t tid); extern int qos_mark_disable (u32 sw_if_index, qos_source_t output_source); +typedef walk_rc_t (*qos_mark_walk_cb_t) (u32 sw_if_index, + u32 map_id, + qos_source_t input_source, + void *ctx); +void qos_mark_walk (qos_mark_walk_cb_t fn, void *c); + #endif /* diff --git a/src/vnet/qos/qos_record.c b/src/vnet/qos/qos_record.c index 20d0b5bf4f5..40e6b0778bb 100644 --- a/src/vnet/qos/qos_record.c +++ b/src/vnet/qos/qos_record.c @@ -100,6 +100,23 @@ qos_record_disable (u32 sw_if_index, qos_source_t input_source) return (0); } +void +qos_record_walk (qos_record_walk_cb_t fn, void *c) +{ + qos_source_t qs; + + FOR_EACH_QOS_SOURCE (qs) + { + u32 sw_if_index; + + vec_foreach_index (sw_if_index, qos_record_configs[qs]) + { + if (0 != qos_record_configs[qs][sw_if_index]) + fn (sw_if_index, qs, c); + } + } +} + /* * Disable recording feature for all protocols when the interface * is deleted @@ -124,7 +141,7 @@ qos_record_ip_interface_add_del (vnet_main_t * vnm, VNET_SW_INTERFACE_ADD_DEL_FUNCTION (qos_record_ip_interface_add_del); clib_error_t * -l2_ip_qos_init (vlib_main_t * vm) +qos_record_init (vlib_main_t * vm) { qos_source_t qs; vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "l2-ip-qos-record"); @@ -139,7 +156,7 @@ l2_ip_qos_init (vlib_main_t * vm) return 0; } -VLIB_INIT_FUNCTION (l2_ip_qos_init); +VLIB_INIT_FUNCTION (qos_record_init); static clib_error_t * qos_record_cli (vlib_main_t * vm, @@ -199,6 +216,87 @@ VLIB_CLI_COMMAND (qos_record_command, static) = { }; /* *INDENT-ON* */ +static void +qos_record_show_one_interface (vlib_main_t * vm, u32 sw_if_index) +{ + u8 n_cfgs[QOS_N_SOURCES] = { }; + qos_source_t qs; + bool set; + + set = false; + + FOR_EACH_QOS_SOURCE (qs) + { + if (vec_len (qos_record_configs[qs]) <= sw_if_index) + continue; + if (0 != (n_cfgs[qs] = qos_record_configs[qs][sw_if_index])) + set = true; + } + + if (set) + { + vlib_cli_output (vm, " %U:", format_vnet_sw_if_index_name, + vnet_get_main (), sw_if_index); + + FOR_EACH_QOS_SOURCE (qs) + { + if (n_cfgs[qs] != 0) + vlib_cli_output (vm, " %U", format_qos_source, qs); + } + } +} + +static clib_error_t * +qos_record_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vnet_main_t *vnm = vnet_get_main (); + qos_source_t qs; + u32 sw_if_index; + + sw_if_index = ~0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%U", unformat_vnet_sw_interface, + vnm, &sw_if_index)) + ; + } + + if (~0 == sw_if_index) + { + u32 ii, n_ints = 0; + + FOR_EACH_QOS_SOURCE (qs) + { + n_ints = clib_max (n_ints, vec_len (qos_record_configs[qs])); + } + + for (ii = 0; ii < n_ints; ii++) + { + qos_record_show_one_interface (vm, ii); + } + } + else + qos_record_show_one_interface (vm, sw_if_index); + + return (NULL); +} + +/*? + * Show Egress Qos Maps + * + * @cliexpar + * @cliexcmd{show qos egress map} + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (qos_record_show_command, static) = { + .path = "show qos record", + .short_help = "show qos record [interface]", + .function = qos_record_show, + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ /* * fd.io coding-style-patch-verification: ON diff --git a/src/vnet/qos/qos_record.h b/src/vnet/qos/qos_record.h index e5726d650a7..b80041f3241 100644 --- a/src/vnet/qos/qos_record.h +++ b/src/vnet/qos/qos_record.h @@ -21,6 +21,11 @@ extern int qos_record_disable (u32 sw_if_index, qos_source_t input_source); extern int qos_record_enable (u32 sw_if_index, qos_source_t input_source); +typedef walk_rc_t (*qos_record_walk_cb_t) (u32 sw_if_index, + qos_source_t input_source, + void *ctx); +void qos_record_walk (qos_record_walk_cb_t fn, void *c); + #endif /* diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c index 840889d6cc3..11ff41aa86c 100644 --- a/src/vpp/api/custom_dump.c +++ b/src/vpp/api/custom_dump.c @@ -3765,9 +3765,9 @@ static void *vl_api_qos_record_enable_disable_t_print u8 *s; s = format (0, "SCRIPT: qos_record_enable_disable "); - s = format (s, "sw_if_index %d ", ntohl (mp->sw_if_index)); + s = format (s, "sw_if_index %d ", ntohl (mp->record.sw_if_index)); s = format (s, "input_source %U ", format_qos_source, - ntohl (mp->input_source)); + mp->record.input_source); if (!mp->enable) s = format (s, "disable "); diff --git a/test/test_qos.py b/test/test_qos.py index 94062b89ae2..9efa79854bf 100644 --- a/test/test_qos.py +++ b/test/test_qos.py @@ -15,6 +15,7 @@ from scapy.layers.inet import IP, UDP from scapy.layers.inet6 import IPv6 from scapy.contrib.mpls import MPLS from vpp_papi import VppEnum +from vpp_qos import VppQosRecord, VppQosEgressMap, VppQosMark NUM_PKTS = 67 @@ -77,7 +78,7 @@ class TestQOS(VppTestCase): {'outputs': os}, {'outputs': os}] - self.vapi.qos_egress_map_update(1, rows) + qem1 = VppQosEgressMap(self, 1, rows).add_vpp_config() # # For table 2 (and up) use the value n for everything @@ -89,7 +90,7 @@ class TestQOS(VppTestCase): {'outputs': os}, {'outputs': os}] - self.vapi.qos_egress_map_update(2, rows) + qem2 = VppQosEgressMap(self, 2, rows).add_vpp_config() output = [scapy.compat.chb(3)] * 256 os = b''.join(output) @@ -98,7 +99,7 @@ class TestQOS(VppTestCase): {'outputs': os}, {'outputs': os}] - self.vapi.qos_egress_map_update(3, rows) + qem3 = VppQosEgressMap(self, 3, rows).add_vpp_config() output = [scapy.compat.chb(4)] * 256 os = b''.join(output) @@ -106,32 +107,29 @@ class TestQOS(VppTestCase): {'outputs': os}, {'outputs': os}, {'outputs': os}] - self.vapi.qos_egress_map_update(4, rows) - self.vapi.qos_egress_map_update(5, rows) - self.vapi.qos_egress_map_update(6, rows) - self.vapi.qos_egress_map_update(7, rows) + qem4 = VppQosEgressMap(self, 4, rows).add_vpp_config() + qem5 = VppQosEgressMap(self, 5, rows).add_vpp_config() + qem6 = VppQosEgressMap(self, 6, rows).add_vpp_config() + qem7 = VppQosEgressMap(self, 7, rows).add_vpp_config() + + self.assertTrue(qem7.query_vpp_config()) self.logger.info(self.vapi.cli("sh qos eg map")) # # Bind interface pgN to table n # - self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 1, - 1) - self.vapi.qos_mark_enable_disable(self.pg2.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 2, - 1) - self.vapi.qos_mark_enable_disable(self.pg3.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 3, - 1) - self.vapi.qos_mark_enable_disable(self.pg4.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 4, - 1) + qm1 = VppQosMark(self, self.pg1, qem1, + self.QOS_SOURCE.QOS_API_SOURCE_IP).add_vpp_config() + qm2 = VppQosMark(self, self.pg2, qem2, + self.QOS_SOURCE.QOS_API_SOURCE_IP).add_vpp_config() + qm3 = VppQosMark(self, self.pg3, qem3, + self.QOS_SOURCE.QOS_API_SOURCE_IP).add_vpp_config() + qm4 = VppQosMark(self, self.pg4, qem4, + self.QOS_SOURCE.QOS_API_SOURCE_IP).add_vpp_config() + self.assertTrue(qm3.query_vpp_config()) + + self.logger.info(self.vapi.cli("sh qos mark")) # # packets ingress on Pg0 @@ -160,9 +158,10 @@ class TestQOS(VppTestCase): # # Enable QoS recording on IP input for pg0 # - self.vapi.qos_record_enable_disable(self.pg0.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 1) + qr1 = VppQosRecord(self, self.pg0, + self.QOS_SOURCE.QOS_API_SOURCE_IP) + qr1.add_vpp_config() + self.logger.info(self.vapi.cli("sh qos record")) # # send the same packets, this time expect the input TOS of 1 @@ -218,14 +217,11 @@ class TestQOS(VppTestCase): # # remove the map on pg2 and pg3, now expect an unchanged IP tos # - self.vapi.qos_mark_enable_disable(self.pg2.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 2, - 0) - self.vapi.qos_mark_enable_disable(self.pg3.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 3, - 0) + qm2.remove_vpp_config() + qm3.remove_vpp_config() + self.logger.info(self.vapi.cli("sh qos mark")) + + self.assertFalse(qm3.query_vpp_config()) self.logger.info(self.vapi.cli("sh int feat pg2")) p_v4[IP].dst = self.pg2.remote_ip4 @@ -249,9 +245,8 @@ class TestQOS(VppTestCase): # # disable the input recording on pg0 # - self.vapi.qos_record_enable_disable(self.pg0.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 0) + self.assertTrue(qr1.query_vpp_config()) + qr1.remove_vpp_config() # # back to an unchanged TOS value @@ -263,14 +258,8 @@ class TestQOS(VppTestCase): # # disable the egress map on pg1 and pg4 # - self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 1, - 0) - self.vapi.qos_mark_enable_disable(self.pg4.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 4, - 0) + qm1.remove_vpp_config() + qm4.remove_vpp_config() # # unchanged Tos on pg1 @@ -279,17 +268,6 @@ class TestQOS(VppTestCase): for p in rx: self.assertEqual(p[IP].tos, 254) - # - # clean-up the map - # - self.vapi.qos_egress_map_delete(1) - self.vapi.qos_egress_map_delete(4) - self.vapi.qos_egress_map_delete(2) - self.vapi.qos_egress_map_delete(3) - self.vapi.qos_egress_map_delete(5) - self.vapi.qos_egress_map_delete(6) - self.vapi.qos_egress_map_delete(7) - def test_qos_mpls(self): """ QoS Mark/Record MPLS """ @@ -313,7 +291,7 @@ class TestQOS(VppTestCase): {'outputs': os3}, {'outputs': os4}] - self.vapi.qos_egress_map_update(1, rows) + qem1 = VppQosEgressMap(self, 1, rows).add_vpp_config() # # a route with 1 MPLS label @@ -337,13 +315,10 @@ class TestQOS(VppTestCase): # enable IP QoS recording on the input Pg0 and MPLS egress marking # on Pg1 # - self.vapi.qos_record_enable_disable(self.pg0.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 1) - self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_MPLS, - 1, - 1) + qr1 = VppQosRecord(self, self.pg0, + self.QOS_SOURCE.QOS_API_SOURCE_IP).add_vpp_config() + qm1 = VppQosMark(self, self.pg1, qem1, + self.QOS_SOURCE.QOS_API_SOURCE_MPLS).add_vpp_config() # # packet that will get one label added and 3 labels added resp. @@ -385,14 +360,12 @@ class TestQOS(VppTestCase): # enable MPLS QoS recording on the input Pg0 and IP egress marking # on Pg1 # - self.vapi.qos_record_enable_disable( - self.pg0.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_MPLS, - 1) - self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 1, - 1) + qr2 = VppQosRecord( + self, self.pg0, + self.QOS_SOURCE.QOS_API_SOURCE_MPLS).add_vpp_config() + qm2 = VppQosMark( + self, self.pg1, qem1, + self.QOS_SOURCE.QOS_API_SOURCE_IP).add_vpp_config() # # MPLS x-connect - COS according to pg1 map @@ -440,26 +413,6 @@ class TestQOS(VppTestCase): for p in rx: self.assertEqual(p[IP].tos, from_mpls) - # - # cleanup - # - self.vapi.qos_record_enable_disable(self.pg0.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 0) - self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_MPLS, - 1, - 0) - self.vapi.qos_record_enable_disable( - self.pg0.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_MPLS, - 0) - self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 1, - 0) - self.vapi.qos_egress_map_delete(1) - def test_qos_vlan(self): """QoS mark/record VLAN """ @@ -475,7 +428,7 @@ class TestQOS(VppTestCase): {'outputs': os}, {'outputs': os}] - self.vapi.qos_egress_map_update(1, rows) + qem1 = VppQosEgressMap(self, 1, rows).add_vpp_config() sub_if = VppDot1QSubint(self, self.pg0, 11) @@ -488,25 +441,22 @@ class TestQOS(VppTestCase): # # enable VLAN QoS recording/marking on the input Pg0 subinterface and # - self.vapi.qos_record_enable_disable( - sub_if.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_VLAN, - 1) - self.vapi.qos_mark_enable_disable(sub_if.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_VLAN, - 1, - 1) + qr_v = VppQosRecord( + self, sub_if, + self.QOS_SOURCE.QOS_API_SOURCE_VLAN).add_vpp_config() + qm_v = VppQosMark( + self, sub_if, qem1, + self.QOS_SOURCE.QOS_API_SOURCE_VLAN).add_vpp_config() # # IP marking/recording on pg1 # - self.vapi.qos_record_enable_disable(self.pg1.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 1) - self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 1, - 1) + qr_ip = VppQosRecord( + self, self.pg1, + self.QOS_SOURCE.QOS_API_SOURCE_IP).add_vpp_config() + qm_ip = VppQosMark( + self, self.pg1, qem1, + self.QOS_SOURCE.QOS_API_SOURCE_IP).add_vpp_config() # # a routes to/from sub-interface @@ -576,22 +526,6 @@ class TestQOS(VppTestCase): sub_if.unconfig_ip4() sub_if.unconfig_ip6() - self.vapi.qos_record_enable_disable( - sub_if.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_VLAN, - 0) - self.vapi.qos_mark_enable_disable(sub_if.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_VLAN, - 1, - 0) - self.vapi.qos_record_enable_disable(self.pg1.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 0) - self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index, - self.QOS_SOURCE.QOS_API_SOURCE_IP, - 1, - 0) - if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 40c6045c517..e40ef79cbf2 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -2125,28 +2125,6 @@ class VppPapiProvider(object): """ GBP VXLAN tunnel add/del """ return self.api(self.papi.gbp_vxlan_tunnel_dump, {}) - def qos_egress_map_update(self, id, outputs): - """ QOS egress map update """ - return self.api(self.papi.qos_egress_map_update, - {'map_id': id, - 'rows': outputs}) - - def qos_egress_map_delete(self, id): - """ QOS egress map delete """ - return self.api(self.papi.qos_egress_map_delete, - {'map_id': id}) - - def qos_mark_enable_disable(self, sw_if_index, - output_source, - map_id, - enable): - """ QOS Mark Enable/Disable """ - return self.api(self.papi.qos_mark_enable_disable, - {'map_id': map_id, - 'sw_if_index': sw_if_index, - 'output_source': output_source, - 'enable': enable}) - def igmp_enable_disable(self, sw_if_index, enable, host): """ Enable/disable IGMP on a given interface """ return self.api(self.papi.igmp_enable_disable, diff --git a/test/vpp_qos.py b/test/vpp_qos.py new file mode 100644 index 00000000000..0577863ef8c --- /dev/null +++ b/test/vpp_qos.py @@ -0,0 +1,110 @@ +""" + QoS + + object abstractions for representing QoS config VPP +""" + +from vpp_object import VppObject + + +class VppQosRecord(VppObject): + """ QoS Record(ing) configuration """ + + def __init__(self, test, intf, source): + self._test = test + self.intf = intf + self.source = source + + def add_vpp_config(self): + self._test.vapi.qos_record_enable_disable( + enable=1, + record={'sw_if_index': self.intf.sw_if_index, + 'input_source': self.source}) + self._test.registry.register(self, self._test.logger) + return self + + def remove_vpp_config(self): + self._test.vapi.qos_record_enable_disable( + enable=0, + record={'sw_if_index': self.intf.sw_if_index, + 'input_source': self.source}) + + def query_vpp_config(self): + rs = self._test.vapi.qos_record_dump() + + for r in rs: + if self.intf.sw_if_index == r.record.sw_if_index and \ + self.source == r.record.input_source: + return True + return False + + def object_id(self): + return ("qos-record-%s-%d" % (self.intf, self.source)) + + +class VppQosEgressMap(VppObject): + """ QoS Egress Map(ping) configuration """ + + def __init__(self, test, id, rows): + self._test = test + self.id = id + self.rows = rows + + def add_vpp_config(self): + self._test.vapi.qos_egress_map_update( + map={'id': self.id, + 'rows': self.rows}) + self._test.registry.register(self, self._test.logger) + return self + + def remove_vpp_config(self): + self._test.vapi.qos_egress_map_delete(id=self.id) + + def query_vpp_config(self): + rs = self._test.vapi.qos_egress_map_dump() + + for r in rs: + if self.id == r.map.id: + return True + return False + + def object_id(self): + return ("qos-map-%d" % (self.id)) + + +class VppQosMark(VppObject): + """ QoS Mark(ing) configuration """ + + def __init__(self, test, intf, map, source): + self._test = test + self.intf = intf + self.source = source + self.map = map + + def add_vpp_config(self): + self._test.vapi.qos_mark_enable_disable( + enable=1, + mark={'sw_if_index': self.intf.sw_if_index, + 'map_id': self.map.id, + 'output_source': self.source}) + self._test.registry.register(self, self._test.logger) + return self + + def remove_vpp_config(self): + self._test.vapi.qos_mark_enable_disable( + enable=0, + mark={'sw_if_index': self.intf.sw_if_index, + 'output_source': self.source}) + + def query_vpp_config(self): + ms = self._test.vapi.qos_mark_dump() + + for m in ms: + if self.intf.sw_if_index == m.mark.sw_if_index and \ + self.source == m.mark.output_source and \ + self.map.id == m.mark.map_id: + return True + return False + + def object_id(self): + return ("qos-mark-%s-%d" % (self.intf, self.source)) -- 2.16.6