On Sat, Jul 19, 2025 at 8:27 AM Sragdhara Datta Chaudhuri <sragdha.chau...@nutanix.com> wrote: > > Network function: > network-function-add <nf-name> <inport> <outport> - Add a new NF > network-function-del <nf-name-or-uuid> - Delete a NF > network-function-list - List all NFs > > Network function group: > network-function-group-add <nfg-name> [<nf1> <nf2> ...] - Add a new NFG > network-function-group-del <nfg-name-or-uuid> - Delete a NFG > network-function-group-list [<nfg-name-or-uuid>] - List all NFGs > network-function-group-add-network-function <nfg-name-or-uuid> > <nf-name-or-uuid> > - Add a NF to a NFG > network-function-group-del-network-function <nfg-name-or-uuid> > <nf-name-or-uuid> > - Remove a NF from a NFG > > ACL (new optional parameter added for NFG): > acl-add <ls>|<pg> <direction> <priority> <match> <action> [<nfg-name-or-uuid>] > > Acked-by: Naveen Yerramneni <naveen.yerramn...@nutanix.com> > Signed-off-by: Sragdhara Datta Chaudhuri <sragdha.chau...@nutanix.com>
Thanks for the patch. You need to document the new commands in ovn-nbctl.8.xml. With this comment addressed: Acked-by:Numan Siddique <num...@ovn.org> Numan > --- > utilities/ovn-nbctl.c | 533 +++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 530 insertions(+), 3 deletions(-) > > diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c > index 56a513217..88e735c39 100644 > --- a/utilities/ovn-nbctl.c > +++ b/utilities/ovn-nbctl.c > @@ -283,7 +283,7 @@ Logical switch commands:\n\ > \n\ > ACL commands:\n\ > [--type={switch | port-group}] [--log] [--severity=SEVERITY] [--name=NAME] > [--may-exist]\n\ > - acl-add {SWITCH | PORTGROUP} DIRECTION PRIORITY MATCH ACTION\n\ > + acl-add {SWITCH | PORTGROUP} DIRECTION PRIORITY MATCH ACTION > [NETWORK-FUNCTION-GROUP]\n\ > add an ACL to SWITCH/PORTGROUP\n\ > [--type={switch | port-group}]\n\ > acl-del {SWITCH | PORTGROUP} [DIRECTION [PRIORITY MATCH]]\n\ > @@ -373,6 +373,26 @@ Forwarding group commands:\n\ > fwd-group-del GROUP delete a forwarding group\n\ > fwd-group-list [SWITCH] print forwarding groups\n\ > \n\ > +Network function group commands:\n\ > + network-function-group-add NETWORK-FUNCTION-GROUP [NETWORK-FUNCTION]...\n\ > + create a network-functionr-group\n\ > + network-function-group-del NETWORK-FUNCTION-GROUP\n\ > + delete a network-function-group\n\ > + network-function-group-list print all network-function-groups\n\ > + network-function-group-add-network-function NETWORK-FUNCTION-GROUP > NETWORK-FUNCTION\n\ > + add a network-function to a\n\ > + network-function-group\n\ > + network-function-group-del-network-function NETWORK-FUNCTION-GROUP > NETWORK-FUNCTION\n\ > + delete a network-function from a\n\ > + network-function-group\n\ > +\n\ > +Network function commands:\n\ > + network-function-add NETWORK-FUNCTION PORT-IN PORT-OUT\n\ > + create a network-function\n\ > + network-function-del NETWORK-FUNCTION delete a network-function\n\ > + network-function-list print all network-functions\n\ > +\n\n",program_name, program_name); > + printf("\ > Logical router commands:\n\ > lr-add [ROUTER] create a logical router named ROUTER\n\ > lr-del ROUTER delete ROUTER and all its ports\n\ > @@ -429,7 +449,7 @@ Policy commands:\n\ > lr-policy-del ROUTER [{PRIORITY | UUID} [MATCH]]\n\ > remove policies from ROUTER\n\ > lr-policy-list ROUTER print policies for ROUTER\n\ > -\n\n",program_name, program_name); > +\n\n"); > printf("\ > NAT commands:\n\ > [--stateless]\n\ > @@ -2135,6 +2155,462 @@ nbctl_lsp_get_ls(struct ctl_context *ctx) > } > } > > + > +static char * OVS_WARN_UNUSED_RESULT > +nf_group_by_name_or_uuid( > + struct ctl_context *ctx, const char *id, > + const bool must_exist, > + const struct nbrec_network_function_group **nf_group_p) > +{ > + const struct nbrec_network_function_group *nf_group = NULL; > + struct uuid nf_group_uuid; > + bool is_uuid = uuid_from_string(&nf_group_uuid, id); > + > + *nf_group_p = NULL; > + if (is_uuid) { > + nf_group = nbrec_network_function_group_get_for_uuid(ctx->idl, > + &nf_group_uuid); > + } > + > + if (!nf_group) { > + const struct nbrec_network_function_group *iter; > + NBREC_NETWORK_FUNCTION_GROUP_FOR_EACH (iter, ctx->idl) { > + if (strcmp(iter->name, id)) { > + continue; > + } > + if (nf_group) { > + return xasprintf("Multiple network function group named > '%s'. " > + "Use a UUID.", id); > + } > + nf_group = iter; > + } > + } > + if (!nf_group && must_exist) { > + return xasprintf("%s: network function group %s not found", id, > + is_uuid ? "UUID" : "name"); > + } > + > + *nf_group_p = nf_group; > + return NULL; > +} > + > +static char * OVS_WARN_UNUSED_RESULT > +nf_by_name_or_uuid( > + struct ctl_context *ctx, const char *id, > + bool must_exist, const struct nbrec_network_function **nf_p) > +{ > + const struct nbrec_network_function *nf = NULL; > + struct uuid nf_uuid; > + bool is_uuid = uuid_from_string(&nf_uuid, id); > + > + *nf_p = NULL; > + if (is_uuid) { > + nf = nbrec_network_function_get_for_uuid(ctx->idl, &nf_uuid); > + } > + > + if (!nf) { > + const struct nbrec_network_function *iter; > + NBREC_NETWORK_FUNCTION_FOR_EACH (iter, ctx->idl) { > + if (strcmp(iter->name, id)) { > + continue; > + } > + if (nf) { > + return xasprintf("Multiple network functions named '%s'. " > + "Use a UUID.", id); > + } > + nf = iter; > + } > + } > + if (!nf && must_exist) { > + return xasprintf("%s: network function %s not found", id, > + is_uuid ? "UUID" : "name"); > + } > + > + *nf_p = nf; > + return NULL; > +} > + > +/* > + * Network Function Groups CLI Functions > + */ > +static void > +nbctl_pre_nf_group_add(struct ctl_context *ctx) > +{ > + ovsdb_idl_add_column(ctx->idl, &nbrec_network_function_col_name); > + ovsdb_idl_add_column(ctx->idl, &nbrec_network_function_group_col_name); > + ovsdb_idl_add_column( > + ctx->idl, &nbrec_network_function_group_col_network_function); > +} > + > +static char * > +set_network_function_in_network_function_group( > + struct ctl_context *ctx, > + const struct nbrec_network_function_group *nf_group, > + char **new_network_function, size_t num_new_network_function) > +{ > + struct nbrec_network_function **nfs; > + nfs = xmalloc(sizeof *nfs * num_new_network_function); > + > + size_t i; > + char *error = NULL; > + for (i = 0; i < num_new_network_function; i++) { > + const struct nbrec_network_function *nf; > + error = nf_by_name_or_uuid(ctx, new_network_function[i], true, &nf); > + if (error) { > + free(nfs); > + return error; > + } > + nfs[i] = (struct nbrec_network_function *) nf; > + } > + nbrec_network_function_group_set_network_function( > + nf_group, nfs, num_new_network_function); > + free(nfs); > + return error; > +} > + > +static void > +nbctl_nf_group_add(struct ctl_context *ctx) > +{ > + const struct nbrec_network_function_group *nf_group; > + const char *nfg_name = ctx->argv[1]; > + > + const bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; > + const bool add_duplicate = shash_find(&ctx->options, > + "--add-duplicate") != NULL; > + > + if (!add_duplicate) { > + NBREC_NETWORK_FUNCTION_GROUP_FOR_EACH (nf_group, ctx->idl) { > + if (!strcmp(nf_group->name, nfg_name)) { > + if (may_exist) { > + return; > + } > + ctl_error(ctx, "%s: a network-function-group with this name " > + "already exists", nfg_name); > + return; > + } > + } > + } > + > + nf_group = nbrec_network_function_group_insert(ctx->txn); > + nbrec_network_function_group_set_name(nf_group, nfg_name); > + nbrec_network_function_group_set_mode(nf_group, "inline"); > + > + if (ctx->argc > 2) { > + ctx->error = set_network_function_in_network_function_group( > + ctx, nf_group, ctx->argv + 2, ctx->argc - 2); > + } > +} > + > +static void > +nbctl_pre_nf_group_del(struct ctl_context *ctx) > +{ > + ovsdb_idl_add_column(ctx->idl, &nbrec_network_function_group_col_name); > +} > + > +static void > +nbctl_nf_group_del(struct ctl_context *ctx) > +{ > + const struct nbrec_network_function_group *nf_group; > + const bool must_exist = !shash_find(&ctx->options, "--if-exists"); > + > + char *error = nf_group_by_name_or_uuid( > + ctx, ctx->argv[1], must_exist, &nf_group); > + if (error) { > + ctx->error = error; > + return; > + } > + if (!nf_group) { > + return; > + } > + > + nbrec_network_function_group_delete(nf_group); > +} > + > +static void > +nbctl_pre_nf_group_list(struct ctl_context *ctx) > +{ > + ovsdb_idl_add_column(ctx->idl, > + &nbrec_network_function_group_col_network_function); > + ovsdb_idl_add_column(ctx->idl, &nbrec_network_function_group_col_name); > +} > + > +static void > +nbctl_nf_group_list(struct ctl_context *ctx) > +{ > + const struct nbrec_network_function_group *nf_group; > + struct smap nf_groups; > + size_t i; > + > + smap_init(&nf_groups); > + > + NBREC_NETWORK_FUNCTION_GROUP_FOR_EACH (nf_group, ctx->idl) { > + smap_add_format(&nf_groups, nf_group->name, > + UUID_FMT " (%s)", > + UUID_ARGS(&nf_group->header_.uuid), > + nf_group->name); > + } > + const struct smap_node **nodes = smap_sort(&nf_groups); > + for (i = 0; i < smap_count(&nf_groups); i++) { > + const struct smap_node *node = nodes[i]; > + ds_put_format(&ctx->output, "%s\n", node->value); > + } > + smap_destroy(&nf_groups); > + free(nodes); > +} > + > +static void > +nbctl_pre_nf_group_add_network_function(struct ctl_context *ctx) > +{ > + ovsdb_idl_add_column(ctx->idl, > + &nbrec_network_function_group_col_network_function); > + ovsdb_idl_add_column(ctx->idl, &nbrec_network_function_group_col_name); > + ovsdb_idl_add_column(ctx->idl, &nbrec_network_function_col_name); > +} > + > +static void > +nbctl_nf_group_add_network_function(struct ctl_context *ctx) > +{ > + const struct nbrec_network_function_group *nf_group; > + const struct nbrec_network_function *nf; > + const bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; > + > + char * error = nf_group_by_name_or_uuid(ctx, ctx->argv[1], true, > + &nf_group); > + if (error) { > + ctx->error = error; > + return; > + } > + if (!nf_group) { > + return; > + } > + > + /* Check that network-function exists */ > + error = nf_by_name_or_uuid(ctx, ctx->argv[2], true, &nf); > + if (error) { > + ctx->error = error; > + return; > + } > + > + /* Do not add network_function more than once in a given > + * network-function-group */ > + for (size_t i = 0; i < nf_group->n_network_function; i++) { > + if (nf_group->network_function[i] == nf) { > + if (!may_exist) { > + ctl_error(ctx, "network-function %s is already added to " > + "network-function-group %s", > + ctx->argv[2], ctx->argv[1]); > + return; > + } > + return; > + } > + } > + > + /* Insert the logical network-function into the logical > + * network-function-group. */ > + nbrec_network_function_group_verify_network_function(nf_group); > + struct nbrec_network_function **new_network_function = > + xmalloc(sizeof *new_network_function > + * (nf_group->n_network_function + 1)); > + memcpy(new_network_function, nf_group->network_function, > + sizeof *new_network_function * nf_group->n_network_function); > + new_network_function[nf_group->n_network_function] = > + CONST_CAST(struct nbrec_network_function *, nf); > + nbrec_network_function_group_set_network_function(nf_group, > + new_network_function, nf_group->n_network_function + 1); > + free(new_network_function); > +} > + > +static void > +remove_nf_from_network_function_group( > + const struct nbrec_network_function_group *nf_group, size_t idx) > +{ > + > + struct nbrec_network_function **new_network_function > + = xmemdup(nf_group->network_function, sizeof *new_network_function * > + nf_group->n_network_function); > + new_network_function[idx] > + = new_network_function[nf_group->n_network_function - 1]; > + nbrec_network_function_group_verify_network_function(nf_group); > + nbrec_network_function_group_set_network_function( > + nf_group, new_network_function, > + nf_group->n_network_function - 1); > + free(new_network_function); > +} > + > +static void > +nbctl_pre_nf_group_del_network_function(struct ctl_context *ctx) > +{ > + ovsdb_idl_add_column(ctx->idl, > + &nbrec_network_function_group_col_network_function); > + ovsdb_idl_add_column(ctx->idl, &nbrec_network_function_group_col_name); > + ovsdb_idl_add_column(ctx->idl, &nbrec_network_function_col_name); > +} > + > +static void > +nbctl_nf_group_del_network_function(struct ctl_context *ctx) > +{ > + const struct nbrec_network_function *nf; > + const struct nbrec_network_function_group *nf_group; > + const bool must_exist = !shash_find(&ctx->options, "--if-exists"); > + > + > + char * error = nf_group_by_name_or_uuid(ctx, ctx->argv[1], must_exist, > + &nf_group); > + if (error) { > + ctx->error = error; > + return; > + } > + if (!nf_group) { > + return; > + } > + > + error = nf_by_name_or_uuid(ctx, ctx->argv[2], must_exist, &nf); > + if (error) { > + ctx->error = error; > + return; > + } > + /* Find the network-function_group that contains 'network-function', > + * then delete it. */ > + > + for (size_t i = 0; i < nf_group->n_network_function; i++) { > + if (nf_group->network_function[i] == nf) { > + remove_nf_from_network_function_group(nf_group,i); > + return; > + } > + } > + if (must_exist) { > + ctl_error(ctx, "network-function %s is not part of any logical > switch", > + ctx->argv[1]); > + return; > + } > +} > +/* End of network-function-group operations */ > + > +/* > + * network-function operations > + */ > +static void > +nbctl_pre_nf_add(struct ctl_context *ctx) > +{ > + nbctl_pre_context(ctx); > + > + ovsdb_idl_add_column(ctx->idl, &nbrec_logical_switch_port_col_name); > + ovsdb_idl_add_column(ctx->idl, &nbrec_network_function_col_name); > + ovsdb_idl_add_column(ctx->idl, &nbrec_network_function_col_inport); > + ovsdb_idl_add_column(ctx->idl, &nbrec_network_function_col_outport); > +} > + > +static void > +nbctl_nf_add(struct ctl_context *ctx) > +{ > + const struct nbrec_logical_switch_port *lsp_in, *lsp_out; > + const struct nbrec_network_function *nf; > + > + const bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; > + const bool add_duplicate = shash_find(&ctx->options, > + "--add-duplicate") != NULL; > + if (may_exist && add_duplicate) { > + ctl_error(ctx, > + "--may-exist and --add-duplicate may not be used > together"); > + return; > + } > + > + char * error = lsp_by_name_or_uuid(ctx, ctx->argv[2], true, &lsp_in); > + if (error) { > + ctx->error = error; > + return; > + } > + error = lsp_by_name_or_uuid(ctx, ctx->argv[3], true, &lsp_out); > + if (error) { > + ctx->error = error; > + return; > + } > + > + const char *nf_name = ctx->argv[1]; > + > + if (!add_duplicate) { > + NBREC_NETWORK_FUNCTION_FOR_EACH (nf, ctx->idl) { > + if (!strcmp(nf->name, nf_name)) { > + if (may_exist) { > + return; > + } > + ctl_error(ctx, "%s: same name network-function already > exists", > + nf_name); > + return; > + } > + } > + } > + > + /* create the logical network-function. */ > + nf = nbrec_network_function_insert(ctx->txn); > + nbrec_network_function_set_inport(nf, lsp_in); > + nbrec_network_function_set_outport(nf, lsp_out); > + nbrec_network_function_set_name(nf, nf_name); > +} > + > +static void > +nbctl_pre_nf_del(struct ctl_context *ctx) > +{ > + ovsdb_idl_add_column(ctx->idl, &nbrec_network_function_col_name); > +} > + > +static void > +nbctl_nf_del(struct ctl_context *ctx) > +{ > + const struct nbrec_network_function *nf; > + const bool must_exist = !shash_find(&ctx->options, "--if-exists"); > + > + char *error = nf_by_name_or_uuid(ctx, ctx->argv[1], must_exist, &nf); > + if (error) { > + ctx->error = error; > + return; > + } > + if (!nf) { > + return; > + } > + nbrec_network_function_delete(nf); > +} > + > +static void > +nbctl_pre_nf_list(struct ctl_context *ctx) > +{ > + ovsdb_idl_add_column(ctx->idl, &nbrec_network_function_col_name); > + ovsdb_idl_add_column(ctx->idl, &nbrec_network_function_col_inport); > + ovsdb_idl_add_column(ctx->idl, &nbrec_network_function_col_outport); > + ovsdb_idl_add_column(ctx->idl, &nbrec_logical_switch_port_col_name); > +} > + > +static void > +nbctl_nf_list(struct ctl_context *ctx) > +{ > + const struct nbrec_network_function *nf; > + struct smap nfs; > + size_t i; > + > + smap_init(&nfs); > + > + NBREC_NETWORK_FUNCTION_FOR_EACH (nf, ctx->idl) { > + const struct nbrec_logical_switch_port *linport = nf->inport; > + const struct nbrec_logical_switch_port *loutport = nf->outport; > + const char *linport_name = linport ? linport->name : "<not_set>"; > + const char *loutport_name = loutport ? loutport->name : "<not_set>"; > + smap_add_format(&nfs, nf->name, > + UUID_FMT " (%s) in:%s out:%s", > + UUID_ARGS(&nf->header_.uuid), > + nf->name, linport_name, loutport_name); > + } > + const struct smap_node **nodes = smap_sort(&nfs); > + for (i = 0; i < smap_count(&nfs); i++) { > + const struct smap_node *node = nodes[i]; > + ds_put_format(&ctx->output, "%s\n", node->value); > + } > + smap_destroy(&nfs); > + free(nodes); > +} > + > +/* End of network-function operations */ > + > + > enum { > DIR_FROM_LPORT, > DIR_TO_LPORT > @@ -2232,6 +2708,10 @@ nbctl_acl_print(struct ctl_context *ctx, const struct > nbrec_acl **acls, > ds_put_format(&ctx->output, "%10s %5"PRId64" (%s) %s", > acl->direction, acl->priority, acl->match, > acl->action); > + if (acl->network_function_group) { > + ds_put_format(&ctx->output, " network-function-group=%s", > + acl->network_function_group->name); > + } > if (acl->log) { > ds_put_cstr(&ctx->output, " log("); > if (acl->name) { > @@ -2405,6 +2885,8 @@ nbctl_pre_acl(struct ctl_context *ctx) > > ovsdb_idl_add_table(ctx->idl, &nbrec_table_sample_collector); > ovsdb_idl_add_table(ctx->idl, &nbrec_table_sample); > + ovsdb_idl_add_column(ctx->idl, &nbrec_acl_col_network_function_group); > + ovsdb_idl_add_column(ctx->idl, &nbrec_network_function_group_col_name); > } > > static void > @@ -2461,10 +2943,21 @@ nbctl_acl_add(struct ctl_context *ctx) > > /* Create the acl. */ > struct nbrec_acl *acl = nbrec_acl_insert(ctx->txn); > + const struct nbrec_network_function_group *network_function_group = NULL; > nbrec_acl_set_priority(acl, priority); > nbrec_acl_set_direction(acl, direction); > nbrec_acl_set_match(acl, ctx->argv[4]); > nbrec_acl_set_action(acl, action); > + if ((ctx->argv[6]) && strcmp(ctx->argv[6], "--")) { > + error = nf_group_by_name_or_uuid(ctx, ctx->argv[6], true, > + &network_function_group); > + if (error) { > + ctx->error = error; > + return; > + } > + > + nbrec_acl_set_network_function_group(acl, network_function_group); > + } > > /* Logging options. */ > bool log = shash_find(&ctx->options, "--log") != NULL; > @@ -8264,7 +8757,8 @@ static const struct ctl_command_syntax nbctl_commands[] > = { > { "ls-list", 0, 0, "", nbctl_pre_ls_list, nbctl_ls_list, NULL, "", RO }, > > /* acl commands. */ > - { "acl-add", 5, 6, "{SWITCH | PORTGROUP} DIRECTION PRIORITY MATCH > ACTION", > + { "acl-add", 5, 7, > + "{SWITCH | PORTGROUP} DIRECTION PRIORITY MATCH ACTION > [NETWORK-FUNCTION-GROUP]", > nbctl_pre_acl, nbctl_acl_add, NULL, > "--log,--may-exist,--type=,--name=,--severity=,--meter=,--label=," > "--apply-after-lb,--tier=,--sample-new=,--sample-est=", RW }, > @@ -8356,6 +8850,39 @@ static const struct ctl_command_syntax > nbctl_commands[] = { > { "lsp-detach-mirror", 2, 2, "PORT MIRROR", nbctl_pre_lsp_mirror, > nbctl_lsp_detach_mirror, NULL, "", RW }, > > + /* network-function-group commands. */ > + { "network-function-group-add", 1, INT_MAX, > + "NETWORK-FUNCTION-GROUP [NETWORK-FUNCTION]", > + nbctl_pre_nf_group_add, > + nbctl_nf_group_add, NULL, "--may-exist,--add-duplicate", RW }, > + { "network-function-group-del", 1, 1, "NETWORK-FUNCTION-GROUP", > + nbctl_pre_nf_group_del, > + nbctl_nf_group_del, > + NULL, "--if-exists", RW }, > + { "network-function-group-list", 0, 1, "[NETWORK-FUNCTION-GROUP]", > + nbctl_pre_nf_group_list, > + nbctl_nf_group_list, > + NULL, "", RO }, > + { "network-function-group-add-network-function", 2, 2, > + "NETWORK-FUNCTION-GROUP NETWORK-FUNCTION", > + nbctl_pre_nf_group_add_network_function, > + nbctl_nf_group_add_network_function, NULL, "--may-exist", RW }, > + { "network-function-group-del-network-function", 2, 2, > + "NETWORK-FUNCTION-GROUP NETWORK-FUNCTION", > + nbctl_pre_nf_group_del_network_function, > + nbctl_nf_group_del_network_function, NULL, "--if-exists", RW }, > + > + /* network-function commands. */ > + { "network-function-add", 3, 3, "NETWORK-FUNCTION, PORT-IN, PORT-OUT", > + nbctl_pre_nf_add, > + nbctl_nf_add, > + NULL, "--may-exist,--add-duplicate", RW }, > + { "network-function-del", 1, 1, "NETWORK-FUNCTION", nbctl_pre_nf_del, > + nbctl_nf_del, > + NULL, "--if-exists", RW }, > + { "network-function-list", 0, 0, "", nbctl_pre_nf_list, > + nbctl_nf_list, NULL, "", RO }, > + > /* forwarding group commands. */ > { "fwd-group-add", 4, INT_MAX, "SWITCH GROUP VIP VMAC PORT...", > nbctl_pre_fwd_group_add, nbctl_fwd_group_add, NULL, "--liveness", RW }, > -- > 2.39.3 > > _______________________________________________ > dev mailing list > d...@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev