@@ -281,7 +281,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\
@@ -362,6 +362,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\
@@ -418,7 +438,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\
@@ -2123,6 +2143,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
@@ -2240,6 +2716,10 @@ nbctl_acl_list(struct ctl_context *ctx)
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) {
@@ -2350,6 +2830,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
@@ -2406,10 +2888,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;
@@ -8024,7 +8517,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 },
@@ -8110,6 +8604,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 },