These changes provide the necessary CLI support in ovn-nbctl to control the SFC. There are new commands for port-chain, port-pair-groups, port-pairs and extensions to ACLs to added a new action 'sfc'.
Co-authored-by: Flavio Fernandes <flavio at flaviof.com> Reported at: https://mail.openvswitch.org/pipermail/ovs-discuss/2016-March/040381.html Reported at: https://mail.openvswitch.org/pipermail/ovs-discuss/2016-May/041359.html Signed-off-by: John McDowall <[email protected]> --- ovn/ovn-nb.xml | 150 +++- ovn/utilities/ovn-nbctl.c | 1935 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 1943 insertions(+), 142 deletions(-) diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml index a3dc916a5..5a85e4ea7 100644 --- a/ovn/ovn-nb.xml +++ b/ovn/ovn-nb.xml @@ -120,7 +120,23 @@ logical port. </p> </column> - + <column name="port_chains"> + <p> + The logical port-chains connected to the logical switch. + </p> + <p> + It is an error for multiple logical switches to include the same + logical port. + </p> + </column> + <column name="port_pairs"> + <p> + The logical chains that define the service path. + </p> + <p> + Logical chains cannot currently cross logical switch boundaries. + </p> + </column> <column name="load_balancer"> Load balance a virtual ipv4 address to a set of logical port endpoint ipv4 addresses. @@ -155,7 +171,98 @@ </column> </group> </table> + <table name="Logical_Port_Chain" title="Logical port chain"> + <p> + Each row represents one logical port chain + </p> + + <column name="name"> + <p> + A name for the logical chain. This name has no special meaning or purpose + other than to provide convenience for human interaction with the ovn-nb + database. There is no requirement for the name to be unique. The + logical chains's UUID should be used as the unique identifier. + </p> + </column> + + <column name="port_pair_groups"> + <p> + The logical list of port pairs that the flow goes through. + </p> + + <p> + It is an error for a port pair group to be empty. + </p> + </column> + <column name="last_hop_port"> + The <ref table="Logical_Switch_Port"/> to be used once packet reaches the end + of the chain. + </column> + <group title="Common Columns"> + <column name="external_ids"> + See <em>External IDs</em> at the beginning of this document. + </column> + </group> + </table> + <table name="Logical_Port_Pair_Group" title="logical port pair groups"> + <p> + An ordered port pair list + </p> + + <column name="name"> + <p> + Logical port pair group name + </p> + </column> + + <column name="port_pairs"> + <p> + port pair for this group + </p> + </column> + + <column name="sortkey"> + <p> + An integer used for ordering instances of <ref table="Logical_Port_Pair_Group"/> + in the <ref column="port_pairs" table="Logical_Port_Chain"/> column + of <ref table="Logical_Port_Chain"/>. + </p> + </column> + <group title="Common Columns"> + <column name="external_ids"> + See <em>External IDs</em> at the beginning of this document. + </column> + </group> + </table> + + <table name="Logical_Port_Pair" title="logical port pairs"> + <p> + Ports pairs defining the service + </p> + <column name="name"> + <p> + Logical port pair + </p> + </column> + + <column name="outport"> + <p> + Out logical port for this port pair. Can be the same value as inport. + </p> + </column> + + <column name="inport"> + <p> + In logical port for this port pair. + </p> + </column> + <group title="Common Columns"> + <column name="external_ids"> + See <em>External IDs</em> at the beginning of this document. + </column> + </group> + </table> <table name="Logical_Switch_Port" title="L2 logical switch port"> <p> A port within an L2 logical switch. @@ -853,6 +960,13 @@ ICMP unreachable message for other IP-based protocols. <code>Not implemented--currently treated as drop</code> </li> + + <li> + <code>sfc</code>: Forward the packet into a logical port chain. + The chain to be used -- as well as any other attributes that determine + the behavior of the packet while in the chain -- are provided + via <ref column="options"/>. + </li> </ul> </column> @@ -868,6 +982,40 @@ </p> </column> + <group title="Options"> + <column name = "options"> + This column provides key/value settings specific to the ACL + <ref column="action"/>. The type-specific options are described + individually below. + </column> + + <group title="Options for action sfc"> + <p> + These options apply when <ref column="action"/> is <code>sfc</code>. + </p> + + <column name="options" key="sfc-port-chain"> + Required when <ref column="action"/> is <code>sfc</code>. + The uuid (or name) of the <ref table="Logical_Port_Chain"/> to be used. + </column> + + <column name="options" key="sfc-bidirectional"> + Optional and only applicable when <ref column="action"/> is <code>sfc</code>. + When set with value <code>true</code>, the implementation will also add rules to make packets + go through the chain in reverse direction. A restriction on making bidirectional chains is + that the inport parameter must be present in <ref column="match"/>, as it will be used as the + <ref table="Logical_Port_Chain" column="last_hop_port"/>. As expected, all <code>src*</code> + fields in <ref column="match"/> will be converted to <code>dst*</code> in order to derive the + reverse ACL. + + <p> + sfc-bidirectional option is not yet implemented. + </p> + </column> + </group> + + </group> + <group title="Common Columns"> <column name="external_ids"> See <em>External IDs</em> at the beginning of this document. diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c index af1eeab7f..dc3d45eea 100644 --- a/ovn/utilities/ovn-nbctl.c +++ b/ovn/utilities/ovn-nbctl.c @@ -323,8 +323,73 @@ Logical switch commands:\n\ ls-del SWITCH delete SWITCH and all its ports\n\ ls-list print the names of all logical switches\n\ \n\ +Logical port-chain commands:\n\ + lport-chain-add LSWITCH [LPORT-CHAIN] create a logical port-chain named LPORT-CHAIN\n\ + lport-chain-del LPORT-CHAIN delete LPORT-CHAIN but not FLOW-CLASSIFIER\n \ + lport-chain-list LSWITCH print the names of all logical port-chains on LSWITCH\n\ +\n\ +Logical port-pair-groups commands:\n\ + lport-pair-group-add LPORT-CHAIN LPORT-PAIR-GROUP-NAME\n\ + create a logical port-pair-group \n\ + lport-pair-group-del LPORT-PAIR-GROUP-NAME delete a port-pair-group, does not delete port-pairs\n\ + or flow-classifier\n\ + lport-pair-group-list LPORT-CHAIN print the names of all logical port-pair-groups\n\ + lport-pair-group-add-port-pair LPORT-PAIR-GROUP LPORT-PAIR add a port pair to a port-group\n\ + lport-pair-group-del-port-pair LPORT-PAIR-GROUP LPORT-PAIR del a port pair from a port-group\n\ +\n\ +Logical port-pair commands:\n\ + lport-pair-add LSWITCH LIN-PORT LOUT-PORT [LPORT-PAIR-NAME]\n\ + create a logical port-pair \n\ + lport-pair-del LPORT-PAIR-NAME delete a port-pair, does not delete ports\n\ + lport-pair-list print the names of all logical port-pairs\n\ +\n\ +Logical flow-classifier commands:\n\ + lflow-classifier-add LPORT-CHAIN LIN-PORT [LFLOW-CLASSIFIER-NAME]\n\ + create a logical flow-classifer \n\ + lflow-classifier-del LFLOW-CLASSIFIER-NAME delete a flow-classifier, does not delete ports\n\ + lflow-classifier-list LPORT-CHAIN print the names of all logical flow-classifiers on a switch\n\ + lflow-classifier-set-logical-destination-port LFLOW_CLASSIFIER [LDEST_PORT]\n\ + set the name of ldest port \n\ + lflow-classifier-get-logical-destination-port LFLOW_CLASSIFIER\n\ + get the name of ldest port \n\ +\n\ +Logical router commands:\n\ + lrouter-add [LROUTER] create a logical router named LROUTER\n\ + lrouter-del LROUTER delete LROUTER and all its ports\n\ + lrouter-list print the names of all logical routers\n\ +\n\ +Logical port-chain commands:\n\ + lsp-chain-add LSWITCH [LSP-CHAIN] create a logical port-chain named LSP-CHAIN\n\ + lsp-chain-del LSP-CHAIN delete LSP-CHAIN but not FLOW-CLASSIFIER\n \ + lsp-chain-list LSWITCH print the names of all logical port-chains on LSWITCH\n\ +\n\ +Logical port-pair-groups commands:\n\ + lsp-pair-group-add LSP-CHAIN LSP-PAIR-GROUP-NAME\n\ + create a logical port-pair-group \n\ + lsp-pair-group-del LSP-PAIR-GROUP-NAME delete a port-pair-group, does not delete port-pairs\n\ + or flow-classifier\n\ + lsp-pair-group-list LSP-CHAIN print the names of all logical port-pair-groups\n\ + lsp-pair-group-add-port-pair LSP-PAIR-GROUP LSP-PAIR add a port pair to a port-group\n\ + lsp-pair-group-del-port-pair LSP-PAIR-GROUP LSP-PAIR del a port pair from a port-group\n\ +\n\ +Logical port-pair commands:\n\ + lsp-pair-add LSWITCH LIN-PORT LOUT-PORT [LSP-PAIR-NAME]\n\ + create a logical port-pair \n\ + lsp-pair-del LSP-PAIR-NAME delete a port-pair, does not delete ports\n\ + lsp-pair-list print the names of all logical port-pairs\n\ +\n\ +Logical flow-classifier commands:\n\ + lflow-classifier-add LSP-CHAIN LIN-PORT [LFLOW-CLASSIFIER-NAME]\n\ + create a logical flow-classifer \n\ + lflow-classifier-del LFLOW-CLASSIFIER-NAME delete a flow-classifier, does not delete ports\n\ + lflow-classifier-list LSP-CHAIN print the names of all logical flow-classifiers on a switch\n\ + lflow-classifier-set-logical-destination-port LFLOW_CLASSIFIER [LDEST_PORT]\n\ + set the name of ldest port \n\ + lflow-classifier-get-logical-destination-port LFLOW_CLASSIFIER\n\ + get the name of ldest port \n\ +\n\ ACL commands:\n\ - acl-add SWITCH DIRECTION PRIORITY MATCH ACTION [log]\n\ + acl-add SWITCH DIRECTION PRIORITY MATCH ACTION [ACL-OPTIONS] [log]\n\ add an ACL to SWITCH\n\ acl-del SWITCH [DIRECTION [PRIORITY MATCH]]\n\ remove ACLs from SWITCH\n\ @@ -360,6 +425,28 @@ Logical switch port commands:\n\ set dhcpv4 options for PORT\n\ lsp-get-dhcpv4-options PORT get the dhcpv4 options for PORT\n\ \n\ +Logical port-chain commands:\n\ + lsp-chain-add SWITCH [CHAIN] LAST_PORT create a logical port-chain [named LSP-CHAIN]\n\ + that has LAST_PORT as last hop at the end of chain\n\ + lsp-chain-del CHAIN delete LSP-CHAIN\n\ + lsp-chain-list [SWITCH] print the names of all logical port-chains [on SWITCH]\n\ + lsp-chain-show SWITCH [CHAIN] print details on port-chains on SWITCH\n\ +\n\ +Logical port-pair-groups commands:\n\ + lsp-pair-group-add CHAIN [PAIR-GROUP [OFFSET]]\n\ + create a logical port-pair-group. Optionally, indicate the order it\n\ + should be in chain.\n\ + lsp-pair-group-del PAIR-GROUP delete a port-pair-group, does not delete port-pairs\n\ + lsp-pair-group-list CHAIN print port-pair-groups for a givan chain\n\ + lsp-pair-group-add-port-pair PAIR-GROUP LSP-PAIR add a port pair to a port-pair-group\n\ + lsp-pair-group-del-port-pair PAIR-GROUP LSP-PAIR del a port pair from a port-pair-group\n\ +\n\ +Logical port-pair commands:\n\ + lsp-pair-add SWITCH PORT-IN PORT-OUT [LSP-PAIR]\n\ + create a logical port-pair\n\ + lsp-pair-del LSP-PAIR delete a port-pair, does not delete ports\n\ + lsp-pair-list [SWITCH [LSP-PAIR]] print the names of all logical port-pairs\n\ +\n\ Logical router commands:\n\ lr-add [ROUTER] create a logical router named ROUTER\n\ lr-del ROUTER delete ROUTER and all its ports\n\ @@ -522,6 +609,119 @@ ls_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist) return ls; } +static const struct nbrec_logical_port_chain * +lsp_chain_by_name_or_uuid(struct ctl_context *ctx, const char *id) +{ + const struct nbrec_logical_port_chain *lsp_chain = NULL; + bool is_uuid = false; + struct uuid lsp_chain_uuid; + + if (uuid_from_string(&lsp_chain_uuid, id)) { + is_uuid = true; + lsp_chain = nbrec_logical_port_chain_get_for_uuid(ctx->idl, + &lsp_chain_uuid); + printf("found lsp_chain %s\n",id); // FIXME(ff): debug, remove this + } + + if (!lsp_chain) { + NBREC_LOGICAL_PORT_CHAIN_FOR_EACH(lsp_chain, ctx->idl) { + if (!strcmp(lsp_chain->name, id)) { + break; + } + } + } + if (!lsp_chain) { + ctl_fatal("lsp_chain not found for %s: '%s'", + is_uuid ? "UUID" : "name", id); + } + + return lsp_chain; +} +static const struct nbrec_logical_port_pair_group * +lsp_pair_group_by_name_or_uuid(struct ctl_context *ctx, const char *id) +{ + const struct nbrec_logical_port_pair_group *lsp_pair_group = NULL; + bool is_uuid = false; + struct uuid lsp_pair_group_uuid; + + if (uuid_from_string(&lsp_pair_group_uuid, id)) { + is_uuid = true; + lsp_pair_group = nbrec_logical_port_pair_group_get_for_uuid(ctx->idl, + &lsp_pair_group_uuid); + printf("Found lsp_pair_group %s\n",id); // FIXME(ff): debug, remove this + } + + if (!lsp_pair_group) { + NBREC_LOGICAL_PORT_PAIR_GROUP_FOR_EACH(lsp_pair_group, ctx->idl) { + if (!strcmp(lsp_pair_group->name, id)) { + break; + } + } + } + if (!lsp_pair_group) { + ctl_fatal("lsp_pair_group not found for %s: '%s'", + is_uuid ? "UUID" : "name", id); + } + + return lsp_pair_group; +} +static const struct nbrec_logical_port_pair * +lsp_pair_by_name_or_uuid(struct ctl_context *ctx, const char *id) +{ + const struct nbrec_logical_port_pair *lsp_pair = NULL; + bool is_uuid = false; + struct uuid lsp_pair_uuid; + + if (uuid_from_string(&lsp_pair_uuid, id)) { + is_uuid = true; + lsp_pair = nbrec_logical_port_pair_get_for_uuid(ctx->idl, + &lsp_pair_uuid); + printf("found lsp_pair %s\n",id); // FIXME(ff): debug, remove this + } + + if (!lsp_pair) { + NBREC_LOGICAL_PORT_PAIR_FOR_EACH(lsp_pair, ctx->idl) { + if (!strcmp(lsp_pair->name, id)) { + break; + } + } + } + if (!lsp_pair) { + ctl_fatal("lsp_pair not found for %s: '%s'", + is_uuid ? "UUID" : "name", id); + } + + return lsp_pair; +} +static const struct nbrec_logical_flow_classifier * +lflow_classifier_by_name_or_uuid(struct ctl_context *ctx, const char *id) +{ + const struct nbrec_logical_flow_classifier *lflow_classifier = NULL; + bool is_uuid = false; + struct uuid lflow_classifier_uuid; + + if (uuid_from_string(&lflow_classifier_uuid, id)) { + is_uuid = true; + lflow_classifier = nbrec_logical_flow_classifier_get_for_uuid(ctx->idl, + &lflow_classifier_uuid); + printf("found lflow_classifier %s\n",id); // FIXME(ff): debug, remove this + } + + if (!lflow_classifier) { + NBREC_LOGICAL_FLOW_CLASSIFIER_FOR_EACH(lflow_classifier, ctx->idl) { + if (!strcmp(lflow_classifier->name, id)) { + break; + } + } + } + if (!lflow_classifier) { + ctl_fatal("lflow_classifier not found for %s: '%s'", + is_uuid ? "UUID" : "name", id); + } + + return lflow_classifier; + } + static const struct nbrec_load_balancer * lb_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist) { @@ -548,217 +748,1560 @@ lb_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist) } } - if (!lb && must_exist) { - ctl_fatal("%s: load balancer %s not found", id, - is_uuid ? "UUID" : "name"); + if (!lb && must_exist) { + ctl_fatal("%s: load balancer %s not found", id, + is_uuid ? "UUID" : "name"); + } + + return lb; +} + +/* Given pointer to logical router, this routine prints the router + * information. */ +static void +print_lr(const struct nbrec_logical_router *lr, struct ds *s) +{ + ds_put_format(s, " router "UUID_FMT" (%s)\n", + UUID_ARGS(&lr->header_.uuid), lr->name); + + for (size_t i = 0; i < lr->n_ports; i++) { + const struct nbrec_logical_router_port *lrp = lr->ports[i]; + ds_put_format(s, " port %s\n", lrp->name); + if (lrp->mac) { + ds_put_cstr(s, " mac: "); + ds_put_format(s, "\"%s\"\n", lrp->mac); + } + if (lrp->n_networks) { + ds_put_cstr(s, " networks: ["); + for (size_t j = 0; j < lrp->n_networks; j++) { + ds_put_format(s, "%s\"%s\"", + j == 0 ? "" : ", ", + lrp->networks[j]); + } + ds_put_cstr(s, "]\n"); + } + } +} + +static void +print_ls(const struct nbrec_logical_switch *ls, struct ds *s) +{ + ds_put_format(s, " switch "UUID_FMT" (%s)\n", + UUID_ARGS(&ls->header_.uuid), ls->name); + + for (size_t i = 0; i < ls->n_ports; i++) { + const struct nbrec_logical_switch_port *lsp = ls->ports[i]; + + ds_put_format(s, " port %s\n", lsp->name); + if (lsp->parent_name) { + ds_put_format(s, " parent: %s\n", lsp->parent_name); + } + if (lsp->n_tag) { + ds_put_format(s, " tag: %"PRIu64"\n", lsp->tag[0]); + } + if (lsp->n_addresses) { + ds_put_cstr(s, " addresses: ["); + for (size_t j = 0; j < lsp->n_addresses; j++) { + ds_put_format(s, "%s\"%s\"", + j == 0 ? "" : ", ", + lsp->addresses[j]); + } + ds_put_cstr(s, "]\n"); + } + } +} + +static void +nbctl_init(struct ctl_context *ctx OVS_UNUSED) +{ +} + +static void +nbctl_pre_sync(struct ctl_context *ctx OVS_UNUSED) +{ + if (wait_type != NBCTL_WAIT_NONE) { + force_wait = true; + } else { + VLOG_INFO("\"sync\" command has no effect without --wait"); + } +} + +static void +nbctl_sync(struct ctl_context *ctx OVS_UNUSED) +{ +} + +static void +nbctl_show(struct ctl_context *ctx) +{ + const struct nbrec_logical_switch *ls; + + if (ctx->argc == 2) { + ls = ls_by_name_or_uuid(ctx, ctx->argv[1], false); + if (ls) { + print_ls(ls, &ctx->output); + } + } else { + NBREC_LOGICAL_SWITCH_FOR_EACH(ls, ctx->idl) { + print_ls(ls, &ctx->output); + } + } + const struct nbrec_logical_router *lr; + + if (ctx->argc == 2) { + lr = lr_by_name_or_uuid(ctx, ctx->argv[1], false); + if (lr) { + print_lr(lr, &ctx->output); + } + } else { + NBREC_LOGICAL_ROUTER_FOR_EACH(lr, ctx->idl) { + print_lr(lr, &ctx->output); + } + } +} + +static void +nbctl_ls_add(struct ctl_context *ctx) +{ + const char *ls_name = ctx->argc == 2 ? ctx->argv[1] : NULL; + + bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; + bool add_duplicate = shash_find(&ctx->options, "--add-duplicate") != NULL; + if (may_exist && add_duplicate) { + ctl_fatal("--may-exist and --add-duplicate may not be used together"); + } + + if (ls_name) { + if (!add_duplicate) { + const struct nbrec_logical_switch *ls; + NBREC_LOGICAL_SWITCH_FOR_EACH (ls, ctx->idl) { + if (!strcmp(ls->name, ls_name)) { + if (may_exist) { + return; + } + ctl_fatal("%s: a switch with this name already exists", + ls_name); + } + } + } + } else if (may_exist) { + ctl_fatal("--may-exist requires specifying a name"); + } else if (add_duplicate) { + ctl_fatal("--add-duplicate requires specifying a name"); + } + + struct nbrec_logical_switch *ls; + ls = nbrec_logical_switch_insert(ctx->txn); + if (ls_name) { + nbrec_logical_switch_set_name(ls, ls_name); + } +} + +static void +nbctl_ls_del(struct ctl_context *ctx) +{ + bool must_exist = !shash_find(&ctx->options, "--if-exists"); + const char *id = ctx->argv[1]; + const struct nbrec_logical_switch *ls; + + ls = ls_by_name_or_uuid(ctx, id, must_exist); + if (!ls) { + return; + } + + nbrec_logical_switch_delete(ls); +} + +static void +nbctl_ls_list(struct ctl_context *ctx) +{ + const struct nbrec_logical_switch *ls; + struct smap lswitches; + + smap_init(&lswitches); + NBREC_LOGICAL_SWITCH_FOR_EACH(ls, ctx->idl) { + smap_add_format(&lswitches, ls->name, UUID_FMT " (%s)", + UUID_ARGS(&ls->header_.uuid), ls->name); + } + const struct smap_node **nodes = smap_sort(&lswitches); + for (size_t i = 0; i < smap_count(&lswitches); i++) { + const struct smap_node *node = nodes[i]; + ds_put_format(&ctx->output, "%s\n", node->value); + } + smap_destroy(&lswitches); + free(nodes); +} + +/* + * Port chain CLI Functions + */ +static void +nbctl_lsp_chain_add(struct ctl_context *ctx) +{ + + const struct nbrec_logical_switch *lswitch; + + if (ctx->argc < 2) { + /* ensure all arguments are present */ + ctl_fatal("Invalid number of arguments: (%d), to lsp-chain-add.",ctx->argc); + } + + const char *lsp_chain_name = ctx->argc == 3 ? ctx->argv[2] : NULL; + lswitch = ls_by_name_or_uuid(ctx, ctx->argv[1], true); + + if (lsp_chain_name) { + const struct nbrec_logical_port_chain *lsp_chain; + NBREC_LOGICAL_PORT_CHAIN_FOR_EACH(lsp_chain, ctx->idl) { + if (strcmp(lsp_chain->name, lsp_chain_name)) + ctl_fatal("%s: an lsp_chain with this name already exists", + lsp_chain_name); + } + } + struct nbrec_logical_port_chain *lsp_chain; + lsp_chain = nbrec_logical_port_chain_insert(ctx->txn); + if (lsp_chain_name) { + nbrec_logical_port_chain_set_name(lsp_chain, lsp_chain_name); + } + + /* Insert the logical port-chain into the logical switch. */ + + nbrec_logical_switch_verify_port_chains(lswitch); + struct nbrec_logical_port_chain **new_port_chain = xmalloc(sizeof *new_port_chain * + (lswitch->n_port_chains + 1)); + memcpy(new_port_chain, lswitch->port_chains, sizeof *new_port_chain * lswitch->n_port_chains); + new_port_chain[lswitch->n_port_chains] = CONST_CAST(struct nbrec_logical_port_chain *, lsp_chain); + nbrec_logical_switch_set_port_chains(lswitch, new_port_chain, lswitch->n_port_chains + 1); + free(new_port_chain); +} + +/* Removes lswitch->pair_chain[idx]'. */ +static void +remove_lsp_chain(const struct nbrec_logical_switch *lswitch, size_t idx) +{ + const struct nbrec_logical_port_chain *lsp_chain = lswitch->port_chains[idx]; + + /* First remove 'lsp-chain' from the array of port-chains. This is what will + * actually cause the logical port-chain to be deleted when the transaction is + * sent to the database server (due to garbage collection). */ + struct nbrec_logical_port_chain **new_port_chain + = xmemdup(lswitch->port_chains, sizeof *new_port_chain * lswitch->n_port_chains); + new_port_chain[idx] = new_port_chain[lswitch->n_port_chains - 1]; + nbrec_logical_switch_verify_port_chains(lswitch); + nbrec_logical_switch_set_port_chains(lswitch, new_port_chain, lswitch->n_port_chains - 1); + free(new_port_chain); + + /* Delete 'lsp-chain' from the IDL. This won't have a real effect on the + * database server (the IDL will suppress it in fact) but it means that it + * won't show up when we iterate with NBREC_LOGICAL_PORT_CHAIN_FOR_EACH later. */ + nbrec_logical_port_chain_delete(lsp_chain); +} + +static void +nbctl_lsp_chain_del(struct ctl_context *ctx) +{ + const struct nbrec_logical_port_chain *lsp_chain; + + lsp_chain = lsp_chain_by_name_or_uuid(ctx, ctx->argv[1]); + if (!lsp_chain) { + ctl_fatal("Cannot find lsp_chain: %s\n", ctx->argv[1]); + } + + /* Find the lswitch that contains 'port-chain', then delete it. */ + const struct nbrec_logical_switch *lswitch; + NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->idl) { + for (size_t i = 0; i < lswitch->n_port_chains; i++) { + if (lswitch->port_chains[i] == lsp_chain) { + remove_lsp_chain(lswitch,i); + printf("Deleted lsp-chain: %s\n", ctx->argv[1]); // FIXME(ff): debug, remove this + return; + } + } + } +} + +static const struct nbrec_logical_switch_port * +lsp_by_name_or_uuid(struct ctl_context *ctx, const char *id, + bool must_exist) +{ + const struct nbrec_logical_switch_port *lsp = NULL; + + struct uuid lsp_uuid; + bool is_uuid = uuid_from_string(&lsp_uuid, id); + if (is_uuid) { + lsp = nbrec_logical_switch_port_get_for_uuid(ctx->idl, &lsp_uuid); + } + + if (!lsp) { + NBREC_LOGICAL_SWITCH_PORT_FOR_EACH(lsp, ctx->idl) { + if (!strcmp(lsp->name, id)) { + break; + } + } + } + + if (!lsp && must_exist) { + ctl_fatal("%s: port %s not found", id, is_uuid ? "UUID" : "name"); + } + + return lsp; + +} + +/* + * Port chain CLI Functions + */ +static const struct nbrec_logical_port_chain * +lsp_chain_by_name_or_uuid(struct ctl_context *ctx, const char *id, const bool must_exist) +{ + const struct nbrec_logical_port_chain *lsp_chain = NULL; + bool is_uuid = false; + struct uuid lsp_chain_uuid; + + if (uuid_from_string(&lsp_chain_uuid, id)) { + is_uuid = true; + lsp_chain = nbrec_logical_port_chain_get_for_uuid(ctx->idl, + &lsp_chain_uuid); + } + + if (!lsp_chain) { + NBREC_LOGICAL_PORT_CHAIN_FOR_EACH(lsp_chain, ctx->idl) { + if (!strcmp(lsp_chain->name, id)) { + break; + } + } + } + if (!lsp_chain && must_exist) { + ctl_fatal("lsp_chain not found for %s: '%s'", + is_uuid ? "UUID" : "name", id); + } + + return lsp_chain; +} +static const struct nbrec_logical_port_pair_group * +lsp_pair_group_by_name_or_uuid(struct ctl_context *ctx, const char *id, const bool must_exist) +{ + const struct nbrec_logical_port_pair_group *lsp_pair_group = NULL; + bool is_uuid = false; + struct uuid lsp_pair_group_uuid; + + if (uuid_from_string(&lsp_pair_group_uuid, id)) { + is_uuid = true; + lsp_pair_group = nbrec_logical_port_pair_group_get_for_uuid(ctx->idl, + &lsp_pair_group_uuid); + } + + if (!lsp_pair_group) { + NBREC_LOGICAL_PORT_PAIR_GROUP_FOR_EACH(lsp_pair_group, ctx->idl) { + if (!strcmp(lsp_pair_group->name, id)) { + break; + } + } + } + if (!lsp_pair_group && must_exist) { + ctl_fatal("lsp_pair_group not found for %s: '%s'", + is_uuid ? "UUID" : "name", id); + } + + return lsp_pair_group; +} + +static const struct nbrec_logical_port_pair * +lsp_pair_by_name_or_uuid(struct ctl_context *ctx, const char *id, const bool must_exist) +{ + const struct nbrec_logical_port_pair *lsp_pair = NULL; + bool is_uuid = false; + struct uuid lsp_pair_uuid; + + if (uuid_from_string(&lsp_pair_uuid, id)) { + is_uuid = true; + lsp_pair = nbrec_logical_port_pair_get_for_uuid(ctx->idl, + &lsp_pair_uuid); + } + + if (!lsp_pair) { + NBREC_LOGICAL_PORT_PAIR_FOR_EACH(lsp_pair, ctx->idl) { + if (!strcmp(lsp_pair->name, id)) { + break; + } + } + } + if (!lsp_pair && must_exist) { + ctl_fatal("lsp_pair not found for %s: '%s'", + is_uuid ? "UUID" : "name", id); + } + + return lsp_pair; +} + + +static void +nbctl_lsp_chain_add(struct ctl_context *ctx) +{ + const struct nbrec_logical_switch *lswitch; + const struct nbrec_logical_switch_port *last_hop_lsp; + + lswitch = ls_by_name_or_uuid(ctx, ctx->argv[1], true /*must_exist*/); + const char *lsp_chain_name = ctx->argc > 3 ? ctx->argv[2] : NULL; + const char *last_hop_lsp_name = lsp_chain_name ? ctx->argv[3] : ctx->argv[2]; + + 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_fatal("--may-exist and --add-duplicate may not be used together"); + } + + last_hop_lsp = lsp_by_name_or_uuid(ctx, last_hop_lsp_name, true); + + if (lsp_chain_name) { + if (!add_duplicate) { + const struct nbrec_logical_port_chain *lsp_chain; + NBREC_LOGICAL_PORT_CHAIN_FOR_EACH(lsp_chain, ctx->idl) { + if (!strcmp(lsp_chain->name, lsp_chain_name)) { + if (may_exist) { + return; + } + ctl_fatal("%s: an lsp_chain with this name already exists", + lsp_chain_name); + } + } + } + } else if (may_exist) { + ctl_fatal("--may-exist requires specifying a name"); + } else if (add_duplicate) { + ctl_fatal("--add-duplicate requires specifying a name"); + } + + struct nbrec_logical_port_chain *lsp_chain; + lsp_chain = nbrec_logical_port_chain_insert(ctx->txn); + if (lsp_chain_name) { + nbrec_logical_port_chain_set_name(lsp_chain, lsp_chain_name); + } + nbrec_logical_port_chain_set_last_hop_port(lsp_chain, last_hop_lsp); + + /* Insert the logical port-chain into the logical switch. */ + + nbrec_logical_switch_verify_port_chains(lswitch); + struct nbrec_logical_port_chain **new_port_chain = xmalloc(sizeof *new_port_chain * + (lswitch->n_port_chains + 1)); + memcpy(new_port_chain, lswitch->port_chains, sizeof *new_port_chain * lswitch->n_port_chains); + new_port_chain[lswitch->n_port_chains] = CONST_CAST(struct nbrec_logical_port_chain *, lsp_chain); + nbrec_logical_switch_set_port_chains(lswitch, new_port_chain, lswitch->n_port_chains + 1); + free(new_port_chain); +} + +/* Removes lswitch->pair_chain[idx]'. */ +static void +remove_lsp_chain(const struct nbrec_logical_switch *lswitch, size_t idx) +{ + const struct nbrec_logical_port_chain *lsp_chain = lswitch->port_chains[idx]; + + /* First remove 'lsp-chain' from the array of port-chains. This is what will + * actually cause the logical port-chain to be deleted when the transaction is + * sent to the database server (due to garbage collection). */ + struct nbrec_logical_port_chain **new_port_chain + = xmemdup(lswitch->port_chains, sizeof *new_port_chain * lswitch->n_port_chains); + new_port_chain[idx] = new_port_chain[lswitch->n_port_chains - 1]; + nbrec_logical_switch_verify_port_chains(lswitch); + nbrec_logical_switch_set_port_chains(lswitch, new_port_chain, lswitch->n_port_chains - 1); + free(new_port_chain); + + /* Delete 'lsp-chain' from the IDL. This won't have a real effect on the + * database server (the IDL will suppress it in fact) but it means that it + * won't show up when we iterate with NBREC_LOGICAL_PORT_CHAIN_FOR_EACH later. */ + nbrec_logical_port_chain_delete(lsp_chain); +} + +static void +nbctl_lsp_chain_del(struct ctl_context *ctx) +{ + const struct nbrec_logical_port_chain *lsp_chain; + const bool must_exist = !shash_find(&ctx->options, "--if-exists"); + + lsp_chain = lsp_chain_by_name_or_uuid(ctx, ctx->argv[1], must_exist); + if (!lsp_chain) { + return; + } + + /* Find the lswitch that contains 'port-chain', then delete it. */ + const struct nbrec_logical_switch *lswitch; + NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->idl) { + for (size_t i = 0; i < lswitch->n_port_chains; i++) { + if (lswitch->port_chains[i] == lsp_chain) { + remove_lsp_chain(lswitch,i); + return; + } + } + } +} + +static void +print_lsp_chain_entry(struct ctl_context *ctx, + const struct nbrec_logical_switch *lswitch, + const char *chain_name_filter, + const bool show_switch_name) +{ + struct smap lsp_chains; + size_t i; + + smap_init(&lsp_chains); + for (i = 0; i < lswitch->n_port_chains; i++) { + const struct nbrec_logical_port_chain *lsp_chain = lswitch->port_chains[i]; + if (chain_name_filter && strcmp(chain_name_filter, lsp_chain->name)) { + continue; + } + if (show_switch_name) { + smap_add_format(&lsp_chains, lsp_chain->name, UUID_FMT " (%s:%s)", + UUID_ARGS(&lsp_chain->header_.uuid), + lswitch->name, lsp_chain->name); + } else { + smap_add_format(&lsp_chains, lsp_chain->name, UUID_FMT " (%s)", + UUID_ARGS(&lsp_chain->header_.uuid), lsp_chain->name); + } + } + + const struct smap_node **nodes = smap_sort(&lsp_chains); + for (i = 0; i < smap_count(&lsp_chains); i++) { + const struct smap_node *node = nodes[i]; + ds_put_format(&ctx->output, "%s\n", node->value); + } + smap_destroy(&lsp_chains); + free(nodes); +} + +static void +nbctl_lsp_chain_list(struct ctl_context *ctx) +{ + const char *id = ctx->argc > 1 ? ctx->argv[1] : NULL; + const char *chain_name_filter = ctx->argc > 2 ? ctx->argv[2] : NULL; + const struct nbrec_logical_switch *lswitch; + + if (id) { + lswitch = ls_by_name_or_uuid(ctx, id, true); + print_lsp_chain_entry(ctx, lswitch, chain_name_filter, false); + } else { + NBREC_LOGICAL_SWITCH_FOR_EACH(lswitch, ctx->idl) { + if (lswitch->n_port_chains == 0) { + continue; + } + print_lsp_chain_entry(ctx, lswitch, chain_name_filter, true); + } + } +} + +static void +print_lsp_chain(const struct nbrec_logical_port_chain *lsp_chain, + struct ctl_context *ctx) +{ + ds_put_format(&ctx->output, "lsp-chain "UUID_FMT" (%s)\n", + UUID_ARGS(&lsp_chain->header_.uuid), lsp_chain->name); + + for (size_t i = 0; i < lsp_chain->n_port_pair_groups; i++) { + const struct nbrec_logical_port_pair_group *lsp_pair_group + = lsp_chain->port_pair_groups[i]; + ds_put_format(&ctx->output, " lsp-pair-group %s\n", lsp_pair_group->name); + for (size_t j = 0; j < lsp_pair_group->n_port_pairs; j++){ + const struct nbrec_logical_port_pair *lsp_pair = lsp_pair_group->port_pairs[j]; + ds_put_format(&ctx->output, " lsp-pair %s\n", lsp_pair->name); + + const struct nbrec_logical_switch_port *linport = lsp_pair->inport; + if (linport) { + ds_put_format(&ctx->output, " lsp-pair inport "UUID_FMT" (%s)\n", + UUID_ARGS(&linport->header_.uuid), linport->name); + } + + const struct nbrec_logical_switch_port *loutport = lsp_pair->outport; + if (loutport) { + ds_put_format(&ctx->output, " lsp-pair outport "UUID_FMT" (%s)\n", + UUID_ARGS(&loutport->header_.uuid), loutport->name); + } + } + } + + // TODO: iterate ACLs and display the ones that have action 'sfc' and use this lsp_chain +} + +static void +nbctl_lsp_chain_show(struct ctl_context *ctx) +{ + const struct nbrec_logical_port_chain *lsp_chain; + + if (ctx->argc == 2) { + lsp_chain = lsp_chain_by_name_or_uuid(ctx, ctx->argv[1], false); + if (lsp_chain) { + print_lsp_chain(lsp_chain, ctx); + } + } else { + NBREC_LOGICAL_PORT_CHAIN_FOR_EACH(lsp_chain, ctx->idl) { + print_lsp_chain(lsp_chain, ctx); + } + } +} +/* End of port-chain operations */ + +/* + * Port Pair Groups CLI Functions + */ +static void +nbctl_lsp_pair_group_add(struct ctl_context *ctx) +{ + const struct nbrec_logical_port_pair_group *lsp_pair_group; + const char *ppg_name = ctx->argc >= 3 ? ctx->argv[2] : NULL; + + 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_fatal("--may-exist and --add-duplicate may not be used together"); + } + + if (ppg_name) { + if (!add_duplicate) { + NBREC_LOGICAL_PORT_PAIR_GROUP_FOR_EACH(lsp_pair_group, ctx->idl) { + if (!strcmp(lsp_pair_group->name, ppg_name)) { + if (may_exist) { + return; + } + ctl_fatal("%s: an lsp_port_pair_group with this name already exists", + ppg_name); + } + } + } + } else if (may_exist) { + ctl_fatal("--may-exist requires specifying a name"); + } else if (add_duplicate) { + ctl_fatal("--add-duplicate requires specifying a name"); + } + + /* check lsp_chain exists */ + const struct nbrec_logical_port_chain *lsp_chain; + lsp_chain = lsp_chain_by_name_or_uuid(ctx, ctx->argv[1], true); + if (!lsp_chain) { + return; + } + + /* create the logical port-pair-group. */ + lsp_pair_group = nbrec_logical_port_pair_group_insert(ctx->txn); + if (ppg_name) { + nbrec_logical_port_pair_group_set_name(lsp_pair_group, ctx->argv[2]); + } + + int64_t sortkey = (int64_t) lsp_chain->n_port_pair_groups + 1; + if (ctx->argc >= 4) { + sortkey = (int64_t) atoi(ctx->argv[3]); + } + nbrec_logical_port_pair_group_set_sortkey(lsp_pair_group, &sortkey, 1); + + /* Insert the logical port-pair-group into the logical switch. */ + nbrec_logical_port_chain_verify_port_pair_groups(lsp_chain); + struct nbrec_logical_port_pair_group **new_port_pair_group = xmalloc(sizeof *new_port_pair_group * + (lsp_chain->n_port_pair_groups + 1)); + memcpy(new_port_pair_group, lsp_chain->port_pair_groups, sizeof *new_port_pair_group * lsp_chain->n_port_pair_groups); + new_port_pair_group[lsp_chain->n_port_pair_groups] = + CONST_CAST(struct nbrec_logical_port_pair_group *,lsp_pair_group); + nbrec_logical_port_chain_set_port_pair_groups(lsp_chain, new_port_pair_group, lsp_chain->n_port_pair_groups + 1); + free(new_port_pair_group); +} + +/* Removes lsp-pair-group 'lsp_chain->port_pair_group[idx]'. */ +static void +remove_lsp_pair_group(const struct nbrec_logical_port_chain *lsp_chain, size_t idx) +{ + const struct nbrec_logical_port_pair_group *lsp_pair_group = lsp_chain->port_pair_groups[idx]; + + /* First remove 'lsp-pair-group' from the array of port-pair-groups. This is what will + * actually cause the logical port-pair-group to be deleted when the transaction is + * sent to the database server (due to garbage collection). */ + struct nbrec_logical_port_pair_group **new_port_pair_group + = xmemdup(lsp_chain->port_pair_groups, sizeof *new_port_pair_group * lsp_chain->n_port_pair_groups); + new_port_pair_group[idx] = new_port_pair_group[lsp_chain->n_port_pair_groups - 1]; + nbrec_logical_port_chain_verify_port_pair_groups(lsp_chain); + nbrec_logical_port_chain_set_port_pair_groups(lsp_chain, new_port_pair_group, lsp_chain->n_port_pair_groups - 1); + free(new_port_pair_group); + + /* Delete 'lsp-pair-group' from the IDL. This won't have a real effect on the + * database server (the IDL will suppress it in fact) but it means that it + * won't show up when we iterate with NBREC_LOGICAL_PORT_PAIR_GROUP_FOR_EACH later. */ + nbrec_logical_port_pair_group_delete(lsp_pair_group); +} + +static void +nbctl_lsp_pair_group_del(struct ctl_context *ctx) +{ + const struct nbrec_logical_port_pair_group *lsp_pair_group; + const bool must_exist = !shash_find(&ctx->options, "--if-exists"); + + lsp_pair_group = lsp_pair_group_by_name_or_uuid(ctx, ctx->argv[1], must_exist); + if (!lsp_pair_group) { + return; + } + + /* Find the port-chain that contains 'port-pair-group', then delete it. */ + const struct nbrec_logical_port_chain *lsp_chain; + NBREC_LOGICAL_PORT_CHAIN_FOR_EACH (lsp_chain, ctx->idl) { + for (size_t i = 0; i < lsp_chain->n_port_pair_groups; i++) { + if (lsp_chain->port_pair_groups[i] == lsp_pair_group) { + remove_lsp_pair_group(lsp_chain,i); + return; + } + } + } + if (must_exist) { + ctl_fatal("logical port-pair-group %s is not part of any logical port-chain", + ctx->argv[1]); + } +} + +static void +nbctl_lsp_pair_group_list(struct ctl_context *ctx) +{ + const char *id = ctx->argv[1]; + const struct nbrec_logical_port_chain *lsp_chain; + struct smap lsp_pair_groups; + size_t i; + + lsp_chain = lsp_chain_by_name_or_uuid(ctx, id, true); + if (!lsp_chain) { + return; + } + + smap_init(&lsp_pair_groups); + for (i = 0; i < lsp_chain->n_port_pair_groups; i++) { + const struct nbrec_logical_port_pair_group *lsp_pair_group = lsp_chain->port_pair_groups[i]; + smap_add_format(&lsp_pair_groups, lsp_pair_group->name, UUID_FMT " (%s)", + UUID_ARGS(&lsp_pair_group->header_.uuid), lsp_pair_group->name); + } + const struct smap_node **nodes = smap_sort(&lsp_pair_groups); + for (i = 0; i < smap_count(&lsp_pair_groups); i++) { + const struct smap_node *node = nodes[i]; + ds_put_format(&ctx->output, "%s\n", node->value); + } + smap_destroy(&lsp_pair_groups); + free(nodes); +} + +static void +nbctl_lsp_pair_group_add_port_pair(struct ctl_context *ctx) +{ + const struct nbrec_logical_port_pair_group *lsp_pair_group; + const struct nbrec_logical_port_pair *lsp_pair; + const bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; + + lsp_pair_group = lsp_pair_group_by_name_or_uuid(ctx, ctx->argv[1], true); + if (!lsp_pair_group) { + return; + } + + /* Check that port-pair exists */ + lsp_pair = lsp_pair_by_name_or_uuid(ctx, ctx->argv[2], true); + if (!lsp_pair){ + return; + } + + /* Do not add port pair more than once in a given port-pair-group */ + for (size_t i = 0; i < lsp_pair_group->n_port_pairs; i++) { + if (lsp_pair_group->port_pairs[i] == lsp_pair) { + if (!may_exist) { + ctl_fatal("lsp_pair: %s is already added to port-pair-group %s\n", ctx->argv[2], ctx->argv[1]); + } + return; + } + } + + /* Insert the logical port-pair into the logical port-pair-group. */ + nbrec_logical_port_pair_group_verify_port_pairs(lsp_pair_group); + struct nbrec_logical_port_pair **new_port_pair = xmalloc(sizeof *new_port_pair * + (lsp_pair_group->n_port_pairs + 1)); + memcpy(new_port_pair, lsp_pair_group->port_pairs, sizeof *new_port_pair * lsp_pair_group->n_port_pairs); + new_port_pair[lsp_pair_group->n_port_pairs] = CONST_CAST(struct nbrec_logical_port_pair *, lsp_pair); + nbrec_logical_port_pair_group_set_port_pairs(lsp_pair_group, new_port_pair, lsp_pair_group->n_port_pairs + 1); + free(new_port_pair); +} + +/* Removes port-pair from port-pair-groiup but does not delete it'. */ +static void +remove_lsp_pair_from_port_pair_group(const struct nbrec_logical_port_pair_group *lsp_pair_group, size_t idx) +{ + //TODO Check const struct nbrec_logical_port_pair *lsp_pair = lsp_pair_group->port_pairs[idx]; + + /* First remove 'lsp-pair' from the array of port-pairs. This is what will + * actually cause the logical port-pair to be deleted when the transaction is + * sent to the database server (due to garbage collection). */ + struct nbrec_logical_port_pair **new_port_pair + = xmemdup(lsp_pair_group->port_pairs, sizeof *new_port_pair * lsp_pair_group->n_port_pairs); + new_port_pair[idx] = new_port_pair[lsp_pair_group->n_port_pairs - 1]; + nbrec_logical_port_pair_group_verify_port_pairs(lsp_pair_group); + nbrec_logical_port_pair_group_set_port_pairs(lsp_pair_group, new_port_pair, lsp_pair_group->n_port_pairs - 1); + free(new_port_pair); + + /* Do not delete actual port-pair as they are owned by a lswitch and can be reused. */ + //nbrec_logical_port_pair_delete(lsp_pair); +} + +static void +nbctl_lsp_pair_group_del_port_pair(struct ctl_context *ctx) +{ + const struct nbrec_logical_port_pair *lsp_pair; + const bool must_exist = !shash_find(&ctx->options, "--if-exists"); + + lsp_pair = lsp_pair_by_name_or_uuid(ctx, ctx->argv[1], must_exist); + if (!lsp_pair) { + return; + } + + /* Find the port-pair_group that contains 'port-pair', then delete it. */ + const struct nbrec_logical_port_pair_group *lsp_pair_group; + NBREC_LOGICAL_PORT_PAIR_GROUP_FOR_EACH (lsp_pair_group, ctx->idl) { + for (size_t i = 0; i < lsp_pair_group->n_port_pairs; i++) { + if (lsp_pair_group->port_pairs[i] == lsp_pair) { + remove_lsp_pair_from_port_pair_group(lsp_pair_group,i); + return; + } + } + } + if (must_exist) { + ctl_fatal("logical port-pair %s is not part of any logical switch", + ctx->argv[1]); + } +} +/* End of port-pair-group operations */ + +/* + * port-pair operations + */ +static void +nbctl_lsp_pair_add(struct ctl_context *ctx) +{ + const struct nbrec_logical_switch *lswitch; + const struct nbrec_logical_switch_port *lsp_in,*lsp_out; + const struct nbrec_logical_port_pair *lsp_pair; + + const bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; + const bool add_duplicate = shash_find(&ctx->options, "--add-duplicate") != NULL; + + lswitch = ls_by_name_or_uuid(ctx, ctx->argv[1], true); + lsp_in = lsp_by_name_or_uuid(ctx, ctx->argv[2], true); + lsp_out = lsp_by_name_or_uuid(ctx, ctx->argv[3], true); + + const char *lsp_pair_name = ctx->argc >= 5 ? ctx->argv[4] : NULL; + if (may_exist && add_duplicate) { + ctl_fatal("--may-exist and --add-duplicate may not be used together"); + } + + if (lsp_pair_name) { + if (!add_duplicate) { + NBREC_LOGICAL_PORT_PAIR_FOR_EACH(lsp_pair, ctx->idl) { + if (!strcmp(lsp_pair->name, lsp_pair_name)) { + if (may_exist) { + return; + } + ctl_fatal("%s: an lsp_pair with this name already exists", + lsp_pair_name); + } + } + } + } else if (may_exist) { + ctl_fatal("--may-exist requires specifying a name"); + } else if (add_duplicate) { + ctl_fatal("--add-duplicate requires specifying a name"); + } + + /* create the logical port-pair. */ + lsp_pair = nbrec_logical_port_pair_insert(ctx->txn); + nbrec_logical_port_pair_set_inport(lsp_pair, lsp_in); + nbrec_logical_port_pair_set_outport(lsp_pair, lsp_out); + if (lsp_pair_name) { + nbrec_logical_port_pair_set_name(lsp_pair, lsp_pair_name); + } + + /* Insert the logical port-pair into the logical port-pair-group. */ + nbrec_logical_switch_verify_port_pairs(lswitch); + struct nbrec_logical_port_pair **new_port_pair = xmalloc(sizeof *new_port_pair * + (lswitch->n_port_pairs + 1)); + memcpy(new_port_pair, lswitch->port_pairs, sizeof *new_port_pair * lswitch->n_port_pairs); + new_port_pair[lswitch->n_port_pairs] = CONST_CAST(struct nbrec_logical_port_pair *, lsp_pair); + nbrec_logical_switch_set_port_pairs(lswitch, new_port_pair, lswitch->n_port_pairs + 1); + free(new_port_pair); +} +/* Removes lswitch->pair_pair[idx]'. */ +static void +remove_lsp_pair(const struct nbrec_logical_switch *lswitch, size_t idx) +{ + const struct nbrec_logical_port_pair *lsp_pair = lswitch->port_pairs[idx]; + + /* First remove 'lsp-pair' from the array of port-pairs. This is what will + * actually cause the logical port-pair to be deleted when the transaction is + * sent to the database server (due to garbage collection). */ + struct nbrec_logical_port_pair **new_port_pair + = xmemdup(lswitch->port_pairs, sizeof *new_port_pair * lswitch->n_port_pairs); + new_port_pair[idx] = new_port_pair[lswitch->n_port_pairs - 1]; + nbrec_logical_switch_verify_port_pairs(lswitch); + nbrec_logical_switch_set_port_pairs(lswitch, new_port_pair, lswitch->n_port_pairs - 1); + free(new_port_pair); + + /* Delete 'lsp-pair' from the IDL. This won't have a real effect on the + * database server (the IDL will suppress it in fact) but it means that it + * won't show up when we iterate with NBREC_LOGICAL_PORT_PAIR_FOR_EACH later. */ + nbrec_logical_port_pair_delete(lsp_pair); +} + +static void +nbctl_lsp_pair_del(struct ctl_context *ctx) +{ + const struct nbrec_logical_port_pair *lsp_pair; + const bool must_exist = !shash_find(&ctx->options, "--if-exists"); + + lsp_pair = lsp_pair_by_name_or_uuid(ctx, ctx->argv[1], must_exist); + if (!lsp_pair) { + if (must_exist) { + ctl_fatal("Cannot find lsp_pair: %s\n", ctx->argv[1]); + } + } + + /* Find the port-pair_group that contains 'port-pair', then delete it. */ + const struct nbrec_logical_switch *lswitch; + NBREC_LOGICAL_SWITCH_FOR_EACH(lswitch, ctx->idl) { + for (size_t i = 0; i < lswitch->n_port_pairs; i++) { + if (lswitch->port_pairs[i] == lsp_pair) { + remove_lsp_pair(lswitch,i); + return; + } + } + } + if (must_exist) { + ctl_fatal("logical port-pair %s is not part of any logical switch", + ctx->argv[1]); + } +} + +static void +print_lsp_pairs_for_switch(struct ctl_context *ctx, + const struct nbrec_logical_switch *lswitch, + const char *ppair_name_filter, + const bool show_switch_name) +{ + struct smap lsp_pairs; + size_t i; + + smap_init(&lsp_pairs); + for (i = 0; i < lswitch->n_port_pairs; i++) { + const struct nbrec_logical_port_pair *lsp_pair = lswitch->port_pairs[i]; + if (ppair_name_filter && strcmp(ppair_name_filter, lsp_pair->name)) { + continue; + } + const struct nbrec_logical_switch_port *linport = lsp_pair->inport; + const struct nbrec_logical_switch_port *loutport = lsp_pair->outport; + const char *linport_name = linport ? linport->name : "<not_set>"; + const char *loutport_name = loutport ? loutport->name : "<not_set>"; + + if (show_switch_name) { + smap_add_format(&lsp_pairs, lsp_pair->name, UUID_FMT " (%s:%s) in:%s out:%s", + UUID_ARGS(&lsp_pair->header_.uuid), lswitch->name, + lsp_pair->name, linport_name, loutport_name); + } else { + smap_add_format(&lsp_pairs, lsp_pair->name, UUID_FMT " (%s) in:%s out:%s", + UUID_ARGS(&lsp_pair->header_.uuid), + lsp_pair->name, linport_name, loutport_name); + } + } + const struct smap_node **nodes = smap_sort(&lsp_pairs); + for (i = 0; i < smap_count(&lsp_pairs); i++) { + const struct smap_node *node = nodes[i]; + ds_put_format(&ctx->output, "%s\n", node->value); + } + smap_destroy(&lsp_pairs); + free(nodes); +} + +static void +nbctl_lsp_pair_list(struct ctl_context *ctx) +{ + const char *id = ctx->argc > 1 ? ctx->argv[1] : NULL; + const char *pair_name_filter = ctx->argc > 2 ? ctx->argv[2] : NULL; + const struct nbrec_logical_switch *lswitch; + + if (id) { + lswitch = ls_by_name_or_uuid(ctx, id, true); + print_lsp_pairs_for_switch(ctx, lswitch, pair_name_filter, false); + } else { + NBREC_LOGICAL_SWITCH_FOR_EACH(lswitch, ctx->idl) { + if (lswitch->n_port_pairs == 0) { + continue; + } + print_lsp_pairs_for_switch(ctx, lswitch, pair_name_filter, true); + } + } +} +/* End of port-pair operations */ + +static void +nbctl_lsp_chain_list(struct ctl_context *ctx) +{ + const char *id = ctx->argv[1]; + const struct nbrec_logical_switch *lswitch; + struct smap lsp_chains; + size_t i; + + lswitch = ls_by_name_or_uuid(ctx, id, true); + if (!lswitch) { + return; + } + + smap_init(&lsp_chains); + for (i = 0; i < lswitch->n_port_chains; i++) { + const struct nbrec_logical_port_chain *lsp_chain = lswitch->port_chains[i]; + smap_add_format(&lsp_chains, lsp_chain->name, UUID_FMT " (%s)", + UUID_ARGS(&lsp_chain->header_.uuid), lsp_chain->name); + } + const struct smap_node **nodes = smap_sort(&lsp_chains); + for (i = 0; i < smap_count(&lsp_chains); i++) { + const struct smap_node *node = nodes[i]; + ds_put_format(&ctx->output, "%s\n", node->value); + } + smap_destroy(&lsp_chains); + free(nodes); +} + +static void +print_lsp_chain(const struct nbrec_logical_port_chain *lsp_chain, + struct ctl_context *ctx) +{ + const char *port_not_set="Not Set"; + ds_put_format(&ctx->output, " lsp-chain "UUID_FMT" (%s)\n", + UUID_ARGS(&lsp_chain->header_.uuid), lsp_chain->name); + for (size_t i = 0; i < lsp_chain->n_port_pair_groups; i++) { + const struct nbrec_logical_port_pair_group *lsp_pair_group + = lsp_chain->port_pair_groups[i]; + ds_put_format(&ctx->output, " lsp-pair-group %s\n", lsp_pair_group->name); + for (size_t j = 0; j < lsp_pair_group->n_port_pairs; j++){ + const struct nbrec_logical_switch_port *linport; + const struct nbrec_logical_switch_port *loutport; + const struct nbrec_logical_port_pair *lsp_pair + = lsp_pair_group->port_pairs[j]; + ds_put_format(&ctx->output, " lsp-pair %s\n", lsp_pair->name); + linport = lsp_pair->inport; + ds_put_format(&ctx->output, " lsp-pair inport "UUID_FMT" (%s)\n", + UUID_ARGS(&linport->header_.uuid), linport->name); + loutport = lsp_pair->outport; + ds_put_format(&ctx->output, " lsp-pair outport "UUID_FMT" (%s)\n", + UUID_ARGS(&loutport->header_.uuid), loutport->name); + } + } + printf("finished port pairs\n"); // FIXME(ff): debug, remove this + + const struct nbrec_logical_flow_classifier *lflow_classifier = lsp_chain->flow_classifier; + printf("Getting flow classifier: %s\n", lflow_classifier->name); // FIXME(ff): debug, remove this + ds_put_format(&ctx->output, " lflow_classifier %s\n", lflow_classifier->name); + if (lflow_classifier->logical_source_port == NULL){ + ds_put_format(&ctx->output, " logical-source-port %s\n", port_not_set); + } else { + ds_put_format(&ctx->output, " logical-source-port %s\n", lflow_classifier->logical_source_port->name); + } + if (lflow_classifier->logical_destination_port == NULL){ + ds_put_format(&ctx->output, " logical-destination-port %s\n", port_not_set); + } else { + ds_put_format(&ctx->output, " logical-destination-port %s\n", lflow_classifier->logical_destination_port->name); + } + ds_put_format(&ctx->output, " ethertype: %s\n", lflow_classifier->ethertype); + ds_put_format(&ctx->output, " protocol: %s\n", lflow_classifier->protocol); + ds_put_format(&ctx->output, " source_port_range_min: %ld\n", lflow_classifier->source_port_range_min); + ds_put_format(&ctx->output, " source_port_range_max: %ld\n", lflow_classifier->source_port_range_max); + ds_put_format(&ctx->output, " destination_port_range_min: %ld\n", lflow_classifier->destination_port_range_min); + ds_put_format(&ctx->output, " destination_port_range_max: %ld\n", lflow_classifier->destination_port_range_max); + ds_put_format(&ctx->output, " source_ip_prefix: %ld\n", lflow_classifier->source_ip_prefix); + ds_put_format(&ctx->output, " destination_ip_prefix: %ld\n", lflow_classifier->destination_ip_prefix); +} + +static void +nbctl_lsp_chain_show(struct ctl_context *ctx) +{ + const struct nbrec_logical_switch *lswitch; + const struct nbrec_logical_port_chain *lsp_chain; + printf("\nIn lsp-chain-show\n"); // FIXME(ff): debug, remove this + if (ctx->argc < 2) { + /* ensure all arguments are present */ + ctl_fatal("Invalid number of arguments: (%d), to lsp-chain-show.",ctx->argc); + } + lswitch = ls_by_name_or_uuid(ctx, ctx->argv[1], true); + ds_put_format(&ctx->output, " lswitch "UUID_FMT" (%s)\n", + UUID_ARGS(&lswitch->header_.uuid), lswitch->name); + if (ctx->argc == 3) { + lsp_chain = lsp_chain_by_name_or_uuid(ctx, ctx->argv[2]); + if (lsp_chain) { + print_lsp_chain(lsp_chain, ctx); + } + } else { + NBREC_LOGICAL_PORT_CHAIN_FOR_EACH(lsp_chain, ctx->idl) { + print_lsp_chain(lsp_chain, ctx); + } + } +} + + +static void +nbctl_lsp_chain_set_flow_classifier(struct ctl_context *ctx) +{ + + const struct nbrec_logical_port_chain *lsp_chain; + const struct nbrec_logical_flow_classifier *lflow_classifier = NULL; + + if (ctx->argc < 3){ + /* ensure all arguments are present */ + ctl_fatal("Invalid number of arguments: (%d), to lsp-chain-set-flow-classifier.",ctx->argc); + } + + lsp_chain = lsp_chain_by_name_or_uuid(ctx, ctx->argv[1]); + if (!lsp_chain){ + ctl_fatal("Invalid port_chain %s ", ctx->argv[1]); + } + /* Check flow classifier exists*/ + lflow_classifier = lflow_classifier_by_name_or_uuid(ctx, ctx->argv[2]); + if (!lflow_classifier){ + ctl_fatal("Invalid flow_classifier %s ", ctx->argv[2]); + } + + /* Insert the logical flow-classifier into the logical port-chain. */ + nbrec_logical_port_chain_verify_flow_classifier(lsp_chain); + //struct nbrec_logical_flow_classifier **new_flow_classifier= xmalloc(sizeof *new_flow_classifier); + //memcpy(new_flow_classifier, lsp_chain->flow_classifier, sizeof *new_flow_classifier); + //new_flow_classifier = CONST_CAST(struct nbrec_logical_flow_classifier *, lflow_classifier); + //nbrec_logical_port_chain_set_flow_classifier(lsp_chain, new_flow_classifier); + nbrec_logical_port_chain_set_flow_classifier(lsp_chain, lflow_classifier); // FIXME (ff): should allow multiple classifiers to same port_chain + //free(new_flow_classifier); +} + +static void nbctl_lsp_chain_get_flow_classifier(struct ctl_context *ctx) +{ + const char *id = ctx->argv[1]; + const struct nbrec_logical_port_chain *lsp_chain; + + lsp_chain = lsp_chain_by_name_or_uuid(ctx, id); + ds_put_format(&ctx->output, "%s\n", lsp_chain->flow_classifier->name); +} +/* End of port-chain operations */ + +/* + * Port Pair Groups CLI Functions + */ +static void +nbctl_lsp_pair_group_add(struct ctl_context *ctx) +{ + const struct nbrec_logical_port_pair_group *lsp_pair_group; + const struct nbrec_logical_port_chain *lsp_chain; + + /* check lsp_chain exists */ + lsp_chain = lsp_chain_by_name_or_uuid(ctx, ctx->argv[1]); + if (!lsp_chain) { + return; + } + + if (ctx->argc < 2) { + /* ensure all arguments are present */ + ctl_fatal("invalid number of arguments: %d to lsp-pair-groups-add.", ctx->argc); } - return lb; -} + /* create the logical port-pair-group. */ + lsp_pair_group = nbrec_logical_port_pair_group_insert(ctx->txn); + if (ctx->argc == 3){ + nbrec_logical_port_pair_group_set_name(lsp_pair_group, ctx->argv[2]); + } -/* Given pointer to logical router, this routine prints the router - * information. */ + /* Insert the logical port into the logical switch. */ + nbrec_logical_port_chain_verify_port_pair_groups(lsp_chain); + struct nbrec_logical_port_pair_group **new_port_pair_group = xmalloc(sizeof *new_port_pair_group * + (lsp_chain->n_port_pair_groups + 1)); + memcpy(new_port_pair_group, lsp_chain->port_pair_groups, sizeof *new_port_pair_group * lsp_chain->n_port_pair_groups); + new_port_pair_group[lsp_chain->n_port_pair_groups] = + CONST_CAST(struct nbrec_logical_port_pair_group *,lsp_pair_group); + nbrec_logical_port_chain_set_port_pair_groups(lsp_chain, new_port_pair_group, lsp_chain->n_port_pair_groups + 1); + free(new_port_pair_group); +} +/* Removes lsp-pair-group 'lsp_chain->port_pair_group[idx]'. */ static void -print_lr(const struct nbrec_logical_router *lr, struct ds *s) +remove_lsp_pair_group(const struct nbrec_logical_port_chain *lsp_chain, size_t idx) { - ds_put_format(s, " router "UUID_FMT" (%s)\n", - UUID_ARGS(&lr->header_.uuid), lr->name); + const struct nbrec_logical_port_pair_group *lsp_pair_group = lsp_chain->port_pair_groups[idx]; - for (size_t i = 0; i < lr->n_ports; i++) { - const struct nbrec_logical_router_port *lrp = lr->ports[i]; - ds_put_format(s, " port %s\n", lrp->name); - if (lrp->mac) { - ds_put_cstr(s, " mac: "); - ds_put_format(s, "\"%s\"\n", lrp->mac); - } - if (lrp->n_networks) { - ds_put_cstr(s, " networks: ["); - for (size_t j = 0; j < lrp->n_networks; j++) { - ds_put_format(s, "%s\"%s\"", - j == 0 ? "" : ", ", - lrp->networks[j]); - } - ds_put_cstr(s, "]\n"); - } - } + /* First remove 'lsp-pair-group' from the array of port-pair-groups. This is what will + * actually cause the logical port-pair-group to be deleted when the transaction is + * sent to the database server (due to garbage collection). */ + struct nbrec_logical_port_pair_group **new_port_pair_group + = xmemdup(lsp_chain->port_pair_groups, sizeof *new_port_pair_group * lsp_chain->n_port_pair_groups); + new_port_pair_group[idx] = new_port_pair_group[lsp_chain->n_port_pair_groups - 1]; + nbrec_logical_port_chain_verify_port_pair_groups(lsp_chain); + nbrec_logical_port_chain_set_port_pair_groups(lsp_chain, new_port_pair_group, lsp_chain->n_port_pair_groups - 1); + free(new_port_pair_group); + + /* Delete 'lsp-pair-group' from the IDL. This won't have a real effect on the + * database server (the IDL will suppress it in fact) but it means that it + * won't show up when we iterate with NBREC_LOGICAL_PORT_PAIR_GROUP_FOR_EACH later. */ + nbrec_logical_port_pair_group_delete(lsp_pair_group); } static void -print_ls(const struct nbrec_logical_switch *ls, struct ds *s) +nbctl_lsp_pair_group_del(struct ctl_context *ctx) { - ds_put_format(s, " switch "UUID_FMT" (%s)\n", - UUID_ARGS(&ls->header_.uuid), ls->name); + const struct nbrec_logical_port_pair_group *lsp_pair_group; - for (size_t i = 0; i < ls->n_ports; i++) { - const struct nbrec_logical_switch_port *lsp = ls->ports[i]; + lsp_pair_group = lsp_pair_group_by_name_or_uuid(ctx, ctx->argv[1]); + if (!lsp_pair_group) { + ctl_fatal("Cannot find lsp_pair_group: %s\n", ctx->argv[1]); + } - ds_put_format(s, " port %s\n", lsp->name); - if (lsp->parent_name) { - ds_put_format(s, " parent: %s\n", lsp->parent_name); - } - if (lsp->n_tag) { - ds_put_format(s, " tag: %"PRIu64"\n", lsp->tag[0]); - } - if (lsp->n_addresses) { - ds_put_cstr(s, " addresses: ["); - for (size_t j = 0; j < lsp->n_addresses; j++) { - ds_put_format(s, "%s\"%s\"", - j == 0 ? "" : ", ", - lsp->addresses[j]); + /* Find the port-chain that contains 'port-pair-group', then delete it. */ + const struct nbrec_logical_port_chain *lsp_chain; + NBREC_LOGICAL_PORT_CHAIN_FOR_EACH (lsp_chain, ctx->idl) { + for (size_t i = 0; i < lsp_chain->n_port_pair_groups; i++) { + if (lsp_chain->port_pair_groups[i] == lsp_pair_group) { + remove_lsp_pair_group(lsp_chain,i); + printf("Deleted lsp-pair-group: %s\n", ctx->argv[1]); // FIXME(ff): debug, remove this + return; } - ds_put_cstr(s, "]\n"); } } + ctl_fatal("logical port-pair-group %s is not part of any logical port-chain", + ctx->argv[1]); } static void -nbctl_init(struct ctl_context *ctx OVS_UNUSED) +nbctl_lsp_pair_group_list(struct ctl_context *ctx) { + const char *id = ctx->argv[1]; + const struct nbrec_logical_port_chain *lsp_chain; + struct smap lsp_pair_groups; + size_t i; + + lsp_chain = lsp_chain_by_name_or_uuid(ctx, id); + if (!lsp_chain) { + return; + } + + smap_init(&lsp_pair_groups); + for (i = 0; i < lsp_chain->n_port_pair_groups; i++) { + const struct nbrec_logical_port_pair_group *lsp_pair_group = lsp_chain->port_pair_groups[i]; + smap_add_format(&lsp_pair_groups, lsp_pair_group->name, UUID_FMT " (%s)", + UUID_ARGS(&lsp_pair_group->header_.uuid), lsp_pair_group->name); + } + const struct smap_node **nodes = smap_sort(&lsp_pair_groups); + for (i = 0; i < smap_count(&lsp_pair_groups); i++) { + const struct smap_node *node = nodes[i]; + ds_put_format(&ctx->output, "%s\n", node->value); + } + smap_destroy(&lsp_pair_groups); + free(nodes); } static void -nbctl_pre_sync(struct ctl_context *ctx OVS_UNUSED) +nbctl_lsp_pair_group_add_port_pair(struct ctl_context *ctx) { - if (wait_type != NBCTL_WAIT_NONE) { - force_wait = true; - } else { - VLOG_INFO("\"sync\" command has no effect without --wait"); - } + const struct nbrec_logical_port_pair_group *lsp_pair_group; + const struct nbrec_logical_port_pair *lsp_pair; + const char *lsp_pair_name; + + lsp_pair_group = lsp_pair_group_by_name_or_uuid(ctx, ctx->argv[1]); + + if (ctx->argc < 3) { + /* ensure all arguments are present */ + ctl_fatal("Invalid number of arguments: (%d), to lsp-pair-group-add-port-pair.",ctx->argc); + } + /* Check that port-pair exists */ + lsp_pair_name = ctx->argv[2]; + lsp_pair = lsp_pair_by_name_or_uuid(ctx, lsp_pair_name); + if (!lsp_pair){ + ctl_fatal("%s: an lsp-pair with this name does not exist",lsp_pair_name); + } + + /* Insert the logical port-pair into the logical port-pair-group. */ + nbrec_logical_port_pair_group_verify_port_pairs(lsp_pair_group); + struct nbrec_logical_port_pair **new_port_pair = xmalloc(sizeof *new_port_pair * + (lsp_pair_group->n_port_pairs + 1)); + memcpy(new_port_pair, lsp_pair_group->port_pairs, sizeof *new_port_pair * lsp_pair_group->n_port_pairs); + new_port_pair[lsp_pair_group->n_port_pairs] = CONST_CAST(struct nbrec_logical_port_pair *, lsp_pair); + nbrec_logical_port_pair_group_set_port_pairs(lsp_pair_group, new_port_pair, lsp_pair_group->n_port_pairs + 1); + free(new_port_pair); } +/* Removes port-pair from port-pair-groiup but does not delete it'. */ static void -nbctl_sync(struct ctl_context *ctx OVS_UNUSED) +remove_lsp_pair_from_port_pair_group(const struct nbrec_logical_port_pair_group *lsp_pair_group, size_t idx) { + //TODO Check const struct nbrec_logical_port_pair *lsp_pair = lsp_pair_group->port_pairs[idx]; + + /* First remove 'lsp-pair' from the array of port-pairs. This is what will + * actually cause the logical port-pair to be deleted when the transaction is + * sent to the database server (due to garbage collection). */ + struct nbrec_logical_port_pair **new_port_pair + = xmemdup(lsp_pair_group->port_pairs, sizeof *new_port_pair * lsp_pair_group->n_port_pairs); + new_port_pair[idx] = new_port_pair[lsp_pair_group->n_port_pairs - 1]; + nbrec_logical_port_pair_group_verify_port_pairs(lsp_pair_group); + nbrec_logical_port_pair_group_set_port_pairs(lsp_pair_group, new_port_pair, lsp_pair_group->n_port_pairs - 1); + free(new_port_pair); + + /* Do not delete actual port-pair as they are owned by a lswitch and can be reused. */ + //nbrec_logical_port_pair_delete(lsp_pair); } static void -nbctl_show(struct ctl_context *ctx) +nbctl_lsp_pair_group_del_port_pair(struct ctl_context *ctx) { - const struct nbrec_logical_switch *ls; + const struct nbrec_logical_port_pair *lsp_pair; - if (ctx->argc == 2) { - ls = ls_by_name_or_uuid(ctx, ctx->argv[1], false); - if (ls) { - print_ls(ls, &ctx->output); - } - } else { - NBREC_LOGICAL_SWITCH_FOR_EACH(ls, ctx->idl) { - print_ls(ls, &ctx->output); - } + lsp_pair = lsp_pair_by_name_or_uuid(ctx, ctx->argv[1]); + if (!lsp_pair) { + ctl_fatal("Cannot find lsp_pair: %s\n", ctx->argv[1]); } - const struct nbrec_logical_router *lr; - if (ctx->argc == 2) { - lr = lr_by_name_or_uuid(ctx, ctx->argv[1], false); - if (lr) { - print_lr(lr, &ctx->output); - } - } else { - NBREC_LOGICAL_ROUTER_FOR_EACH(lr, ctx->idl) { - print_lr(lr, &ctx->output); + /* Find the port-pair_group that contains 'port-pair', then delete it. */ + const struct nbrec_logical_port_pair_group *lsp_pair_group; + NBREC_LOGICAL_PORT_PAIR_GROUP_FOR_EACH (lsp_pair_group, ctx->idl) { + for (size_t i = 0; i < lsp_pair_group->n_port_pairs; i++) { + if (lsp_pair_group->port_pairs[i] == lsp_pair) { + remove_lsp_pair_from_port_pair_group(lsp_pair_group,i); + printf("Deleted lsp-pair: %s from lsp-group-pair \n", ctx->argv[1]); // FIXME(ff): debug, remove this + return; + } } } + ctl_fatal("logical port-pair %s is not part of any logical switch", + ctx->argv[1]); } +/* End of port-pair-group operations */ +/* + * port-pair operations + */ static void -nbctl_ls_add(struct ctl_context *ctx) +nbctl_lsp_pair_add(struct ctl_context *ctx) { - const char *ls_name = ctx->argc == 2 ? ctx->argv[1] : NULL; + const char *port_id_in = ctx->argv[2]; + const char *port_id_out = ctx->argv[3]; - bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; - bool add_duplicate = shash_find(&ctx->options, "--add-duplicate") != NULL; - if (may_exist && add_duplicate) { - ctl_fatal("--may-exist and --add-duplicate may not be used together"); - } + const struct nbrec_logical_switch *lswitch; + const struct nbrec_logical_port_pair *lsp_pair; + const struct nbrec_logical_switch_port *lsp_in,*lsp_out; - if (ls_name) { - if (!add_duplicate) { - const struct nbrec_logical_switch *ls; - NBREC_LOGICAL_SWITCH_FOR_EACH (ls, ctx->idl) { - if (!strcmp(ls->name, ls_name)) { - if (may_exist) { - return; - } - ctl_fatal("%s: a switch with this name already exists", - ls_name); - } - } - } - } else if (may_exist) { - ctl_fatal("--may-exist requires specifying a name"); - } else if (add_duplicate) { - ctl_fatal("--add-duplicate requires specifying a name"); + lswitch = ls_by_name_or_uuid(ctx, ctx->argv[1], true); + + if (ctx->argc < 4) { + /* ensure all arguments are present */ + ctl_fatal("Invalid number of arguments: (%d), to lsp-pair-add.",ctx->argc); + } + /* Check that ports exist in this switch */ + lsp_in = lsp_by_name_or_uuid(ctx, port_id_in, false); + if (!lsp_in){ + ctl_fatal("%s: an lsp with this name does not exist",ctx->argv[2]); + } + lsp_out = lsp_by_name_or_uuid(ctx, port_id_out, false); + if (!lsp_out){ + ctl_fatal("%s: an lsp with this name does not exist",ctx->argv[3]); } - struct nbrec_logical_switch *ls; - ls = nbrec_logical_switch_insert(ctx->txn); - if (ls_name) { - nbrec_logical_switch_set_name(ls, ls_name); + /* create the logical port-pair. */ + lsp_pair = nbrec_logical_port_pair_insert(ctx->txn); + nbrec_logical_port_pair_set_inport(lsp_pair, lsp_in); + nbrec_logical_port_pair_set_outport(lsp_pair, lsp_out); + if (ctx->argc == 5){ + nbrec_logical_port_pair_set_name(lsp_pair, ctx->argv[4]); } + + /* Insert the logical port-pair into the logical port-pair-group. */ + nbrec_logical_switch_verify_port_pairs(lswitch); + struct nbrec_logical_port_pair **new_port_pair = xmalloc(sizeof *new_port_pair * + (lswitch->n_port_pairs + 1)); + memcpy(new_port_pair, lswitch->port_pairs, sizeof *new_port_pair * lswitch->n_port_pairs); + new_port_pair[lswitch->n_port_pairs] = CONST_CAST(struct nbrec_logical_port_pair *, lsp_pair); + nbrec_logical_switch_set_port_pairs(lswitch, new_port_pair, lswitch->n_port_pairs + 1); + free(new_port_pair); +} +/* Removes lswitch->pair_pair[idx]'. */ +static void +remove_lsp_pair(const struct nbrec_logical_switch *lswitch, size_t idx) +{ + const struct nbrec_logical_port_pair *lsp_pair = lswitch->port_pairs[idx]; + + /* First remove 'lsp-pair' from the array of port-pairs. This is what will + * actually cause the logical port-pair to be deleted when the transaction is + * sent to the database server (due to garbage collection). */ + struct nbrec_logical_port_pair **new_port_pair + = xmemdup(lswitch->port_pairs, sizeof *new_port_pair * lswitch->n_port_pairs); + new_port_pair[idx] = new_port_pair[lswitch->n_port_pairs - 1]; + nbrec_logical_switch_verify_port_pairs(lswitch); + nbrec_logical_switch_set_port_pairs(lswitch, new_port_pair, lswitch->n_port_pairs - 1); + free(new_port_pair); + + /* Delete 'lsp-pair' from the IDL. This won't have a real effect on the + * database server (the IDL will suppress it in fact) but it means that it + * won't show up when we iterate with NBREC_LOGICAL_PORT_PAIR_FOR_EACH later. */ + nbrec_logical_port_pair_delete(lsp_pair); } static void -nbctl_ls_del(struct ctl_context *ctx) +nbctl_lsp_pair_del(struct ctl_context *ctx) { - bool must_exist = !shash_find(&ctx->options, "--if-exists"); - const char *id = ctx->argv[1]; - const struct nbrec_logical_switch *ls; + const struct nbrec_logical_port_pair *lsp_pair; - ls = ls_by_name_or_uuid(ctx, id, must_exist); - if (!ls) { - return; + lsp_pair = lsp_pair_by_name_or_uuid(ctx, ctx->argv[1]); + if (!lsp_pair) { + ctl_fatal("Cannot find lsp_pair: %s\n", ctx->argv[1]); } - nbrec_logical_switch_delete(ls); + /* Find the port-pair_group that contains 'port-pair', then delete it. */ + const struct nbrec_logical_switch *lswitch; + NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->idl) { + for (size_t i = 0; i < lswitch->n_port_pairs; i++) { + if (lswitch->port_pairs[i] == lsp_pair) { + remove_lsp_pair(lswitch,i); + printf("Deleted lsp-pair: %s\n", ctx->argv[1]); // FIXME(ff): debug, remove this + return; + } + } + } + ctl_fatal("logical port-pair %s is not part of any logical switch", + ctx->argv[1]); } static void -nbctl_ls_list(struct ctl_context *ctx) +nbctl_lsp_pair_list(struct ctl_context *ctx) { - const struct nbrec_logical_switch *ls; - struct smap lswitches; + const char *id = ctx->argv[1]; + const struct nbrec_logical_switch *lswitch; + struct smap lsp_pairs; + size_t i; - smap_init(&lswitches); - NBREC_LOGICAL_SWITCH_FOR_EACH(ls, ctx->idl) { - smap_add_format(&lswitches, ls->name, UUID_FMT " (%s)", - UUID_ARGS(&ls->header_.uuid), ls->name); + lswitch = ls_by_name_or_uuid(ctx, id, true); + if (!lswitch) { + return; } - const struct smap_node **nodes = smap_sort(&lswitches); - for (size_t i = 0; i < smap_count(&lswitches); i++) { + + smap_init(&lsp_pairs); + for (i = 0; i < lswitch->n_port_pairs; i++) { + const struct nbrec_logical_port_pair *lsp_pair = lswitch->port_pairs[i]; + smap_add_format(&lsp_pairs, lsp_pair->name, UUID_FMT " (%s)", + UUID_ARGS(&lsp_pair->header_.uuid), lsp_pair->name); + } + const struct smap_node **nodes = smap_sort(&lsp_pairs); + for (i = 0; i < smap_count(&lsp_pairs); i++) { const struct smap_node *node = nodes[i]; ds_put_format(&ctx->output, "%s\n", node->value); } - smap_destroy(&lswitches); + smap_destroy(&lsp_pairs); free(nodes); } - -static const struct nbrec_logical_switch_port * -lsp_by_name_or_uuid(struct ctl_context *ctx, const char *id, - bool must_exist) +/* End of port-pair operations */ +/* + * flow_classifier operations + */ +static void +nbctl_lflow_classifier_add(struct ctl_context *ctx) { - const struct nbrec_logical_switch_port *lsp = NULL; + const struct nbrec_logical_port_chain *lsp_chain; + const struct nbrec_logical_switch_port *lsp; + const char *lsp_name; + const struct nbrec_logical_flow_classifier *lflow_classifier; - struct uuid lsp_uuid; - bool is_uuid = uuid_from_string(&lsp_uuid, id); - if (is_uuid) { - lsp = nbrec_logical_switch_port_get_for_uuid(ctx->idl, &lsp_uuid); + + if (ctx->argc < 3) { + /* ensure all arguments are present */ + ctl_fatal("Invalid number of arguments: (%d), to lflow_classifier-add",ctx->argc); + } + lsp_chain = lsp_chain_by_name_or_uuid(ctx, ctx->argv[1]); + /* Check that logical source port exist in this switch */ + lsp_name = ctx->argv[2]; + lsp = lsp_by_name_or_uuid(ctx, lsp_name, false); + if (!lsp){ + ctl_fatal("%s: a lsp with this name does not exist",lsp_name); } - if (!lsp) { - NBREC_LOGICAL_SWITCH_PORT_FOR_EACH(lsp, ctx->idl) { - if (!strcmp(lsp->name, id)) { - break; - } + /* create the logical flow_classifier. */ + lflow_classifier = nbrec_logical_flow_classifier_insert(ctx->txn); + nbrec_logical_flow_classifier_set_logical_source_port(lflow_classifier, lsp); + if (ctx->argc == 4){ + nbrec_logical_flow_classifier_set_name(lflow_classifier, ctx->argv[3]); + } + + /* Insert the logical flow_classifier into the logical switch. */ + nbrec_logical_port_chain_verify_flow_classifier(lsp_chain); + //struct nbrec_logical_flow_classifier **new_flow_classifier = xmalloc(sizeof *new_flow_classifier); + //memcpy(new_flow_classifier, lswitch->flow_classifiers, sizeof *new_flow_classifier * lswitch->n_flow_classifiers); + //new_flow_classifier[lswitch->n_flow_classifiers] = CONST_CAST(struct nbrec_logical_flow_classifier *, lflow_classifier); + nbrec_logical_port_chain_set_flow_classifier(lsp_chain, lflow_classifier); // FIXME (ff): should allow multiple classifiers to same port_chain + //free(new_flow_classifier); +} +static void +nbctl_lflow_classifier_del(struct ctl_context *ctx) +{ + const struct nbrec_logical_flow_classifier *lflow_classifier; + + lflow_classifier = lflow_classifier_by_name_or_uuid(ctx, ctx->argv[1]); + if (!lflow_classifier) { + printf("Cannot find lflow_classifier: %s\n", ctx->argv[1]); // FIXME(ff): debug, remove this + return; + } + + /* Find the switch that contains 'flow-classifier', then delete it. */ + const struct nbrec_logical_port_chain *lsp_chain; + NBREC_LOGICAL_PORT_CHAIN_FOR_EACH (lsp_chain, ctx->idl) { + if (lsp_chain->flow_classifier == lflow_classifier) { + nbrec_logical_flow_classifier_delete(lflow_classifier); + printf("Deleted lflow-classifier: %s\n", ctx->argv[1]); // FIXME(ff): debug, remove this + return; } } + ctl_fatal("logical flow-classifier %s is not part of any logical switch", + ctx->argv[1]); +} - if (!lsp && must_exist) { - ctl_fatal("%s: port %s not found", id, is_uuid ? "UUID" : "name"); +static void +nbctl_lflow_classifier_list(struct ctl_context *ctx) +{ + const char *id = ctx->argv[1]; + const struct nbrec_logical_port_chain *lsp_chain; + + + lsp_chain = lsp_chain_by_name_or_uuid(ctx, id); + if (!lsp_chain) { + return; } + const struct nbrec_logical_flow_classifier *lflow_classifier = lsp_chain->flow_classifier; + printf("Getting flow classifier: %s\n", lflow_classifier->name); // FIXME(ff): debug, remove this + ds_put_format(&ctx->output, " lflow_classifier %s\n", lflow_classifier->name); + ds_put_format(&ctx->output, " logical-source-port %s\n", lflow_classifier->logical_source_port->name); + ds_put_format(&ctx->output, " ethertype: %s\n", lflow_classifier->ethertype); + ds_put_format(&ctx->output, " protocol: %s\n", lflow_classifier->protocol); - return lsp; } +static void +nbctl_lflow_classifier_set_logical_destination_port(struct ctl_context *ctx) +{ + const char *id = ctx->argv[1]; + const struct nbrec_logical_switch_port *lsp = NULL; + const struct nbrec_logical_flow_classifier *lflow_classifier; + + lflow_classifier = lflow_classifier_by_name_or_uuid(ctx, id); + + /* Check port exists if given*/ + if (!strcmp(ctx->argv[2],"") ){ + lsp = lsp_by_name_or_uuid(ctx, ctx->argv[2], true); + if (!lsp){ + ctl_fatal("Invalid port %s ", ctx->argv[2]); + } + } + nbrec_logical_flow_classifier_set_logical_destination_port(lflow_classifier,lsp); +} + +static void +nbctl_lflow_classifier_get_logical_destination_port(struct ctl_context *ctx) +{ + const char *id = ctx->argv[1]; + const struct nbrec_logical_flow_classifier *lflow_classifier; + + lflow_classifier = lflow_classifier_by_name_or_uuid(ctx, id); + ds_put_format(&ctx->output, "%s\n", (lflow_classifier->logical_destination_port)->name); +} +/* End of flow-classifier operations */ + + + + /* Returns the logical switch that contains 'lsp'. */ static const struct nbrec_logical_switch * lsp_to_ls(const struct ovsdb_idl *idl, @@ -1296,12 +2839,33 @@ nbctl_acl_add(struct ctl_context *ctx) /* Validate action. */ if (strcmp(action, "allow") && strcmp(action, "allow-related") - && strcmp(action, "drop") && strcmp(action, "reject")) { + && strcmp(action, "drop") && strcmp(action, "reject") + && strcmp(action, "sfc")) { ctl_fatal("%s: action must be one of \"allow\", \"allow-related\", " - "\"drop\", and \"reject\"", action); + "\"drop\", \"reject\" and \"sfc\"", action); return; } + /* Validate ACL Options, if there were any provided. */ + struct smap acl_options = SMAP_INITIALIZER(&acl_options); + if (ctx->argc >= 7) { + struct sset acl_options_set; + sset_from_delimited_string(&acl_options_set, ctx->argv[6], " "); + + const char *acl_option_tuple; + SSET_FOR_EACH (acl_option_tuple, &acl_options_set) { + char *key, *value; + value = xstrdup(acl_option_tuple); + key = strsep(&value, "="); + if (value) { + smap_add(&acl_options, key, value); + } + free(key); + } + + sset_destroy(&acl_options_set); + } + /* Create the acl. */ struct nbrec_acl *acl = nbrec_acl_insert(ctx->txn); nbrec_acl_set_priority(acl, priority); @@ -1311,6 +2875,9 @@ nbctl_acl_add(struct ctl_context *ctx) if (shash_find(&ctx->options, "--log") != NULL) { nbrec_acl_set_log(acl, true); } + if (! smap_is_empty(&acl_options)) { + nbrec_acl_set_options(acl, &acl_options); + } /* Insert the acl into the logical switch. */ nbrec_logical_switch_verify_acls(ls); @@ -1319,6 +2886,8 @@ nbctl_acl_add(struct ctl_context *ctx) new_acls[ls->n_acls] = acl; nbrec_logical_switch_set_acls(ls, new_acls, ls->n_acls + 1); free(new_acls); + + smap_destroy(&acl_options); } static void @@ -2978,6 +4547,22 @@ static const struct ctl_table_class tables[] = { {{&nbrec_table_logical_switch, &nbrec_logical_switch_col_name, NULL}, {NULL, NULL, NULL}}}, + {&nbrec_table_logical_port_chain, + {{&nbrec_table_logical_port_chain, &nbrec_logical_port_chain_col_name, NULL}, + {NULL, NULL, NULL}}}, + + {&nbrec_table_logical_port_pair_group, + {{&nbrec_table_logical_port_pair_group, &nbrec_logical_port_pair_group_col_name, NULL}, + {NULL, NULL, NULL}}}, + + {&nbrec_table_logical_port_pair, + {{&nbrec_table_logical_port_pair, &nbrec_logical_port_pair_col_name, NULL}, + {NULL, NULL, NULL}}}, + + {&nbrec_table_logical_flow_classifier, + {{&nbrec_table_logical_flow_classifier, &nbrec_logical_flow_classifier_col_name, NULL}, + {NULL, NULL, NULL}}}, + {&nbrec_table_logical_switch_port, {{&nbrec_table_logical_switch_port, &nbrec_logical_switch_port_col_name, NULL}, @@ -3276,6 +4861,74 @@ static const struct ctl_command_syntax nbctl_commands[] = { { "init", 0, 0, "", NULL, nbctl_init, NULL, "", RW }, { "sync", 0, 0, "", nbctl_pre_sync, nbctl_sync, NULL, "", RO }, { "show", 0, 1, "[SWITCH]", NULL, nbctl_show, NULL, "", RO }, + /* lsp-chain commands. */ + { "lsp-chain-add", 1, 2, "LSWITCH,[LSP-CHAIN]", NULL, nbctl_lsp_chain_add, + NULL, "", RW }, + { "lsp-chain-del", 1, 1, "LSP-CHAIN", NULL, nbctl_lsp_chain_del, + NULL, "--if-exists", RW }, + { "lsp-chain-list", 1, 1, "LSWITCH", NULL, nbctl_lsp_chain_list, NULL, "", RO }, + { "lsp-chain-show", 1, 2, "LSWITCH [LSP-CHAIN]", NULL, nbctl_lsp_chain_show, NULL, "", RO }, + { "lsp-chain-get-flow-classifier", 1, 1, "LSP-CHAIN", NULL, + nbctl_lsp_chain_get_flow_classifier, NULL, "", RO }, + { "lsp-chain-set-flow-classifier", 2, 2, "LSP-CHAIN LFLOW-CLASSIFIER", NULL, + nbctl_lsp_chain_set_flow_classifier, NULL, "", RW }, + + /* lsp-pair-group commands. */ + { "lsp-pair-group-add", 1, 2, "LSP-CHAIN [LSP-PAIR-GROUP]", + NULL, nbctl_lsp_pair_group_add, NULL, "", RW }, + { "lsp-pair-group-del", 2, 2, "LSP-CHAIN, LSP-PAIR-GROUP", NULL, nbctl_lsp_pair_group_del, + NULL, "", RW }, + { "lsp-pair-group-list", 1, 1, "LSP_CHAIN", NULL, nbctl_lsp_pair_group_list, NULL, "", RO }, + { "lsp-pair-group-add-port-pair", 2, 2, "LSP-PAIR-GROUP LSP-PAIR", + NULL, nbctl_lsp_pair_group_add_port_pair, NULL, "", RW }, + { "lsp-pair-group-del-port-pair", 2, 2, "LSP-PAIR-GROUP LSP-PAIR", + NULL, nbctl_lsp_pair_group_del_port_pair, NULL, "", RW }, + + /* lsp-pair commands. */ + { "lsp-pair-add", 3, 4, "LSWITCH, LSP, LSP [LSP_PAIR_NAME]", NULL, nbctl_lsp_pair_add, + NULL, "", RW }, + { "lsp-pair-del", 1, 1, "LSP-PAIR", NULL, nbctl_lsp_pair_del, + NULL, "", RW }, + { "lsp-pair-list", 1, 1, "LSWITCH", NULL, nbctl_lsp_pair_list, NULL, "", RO }, + + /* lflow-classifier commands. */ + { "lflow-classifier-add", 2, 3, "LSP-CHAIN LSOURCE_PORT [LFLOW-CLASSIFIER-NAME]", NULL, + nbctl_lflow_classifier_add, NULL, "", RW }, + { "lflow-classifier-del", 1, 1, "LFLOW-CLASSIFIER", NULL, + nbctl_lflow_classifier_del, NULL, "", RW }, + { "lflow-classifier-list", 1, 1, "LSP-CHAIN", NULL, nbctl_lflow_classifier_list, + NULL, "", RO }, + { "lflow-classifier-get-logical-destination-port", 1, 1, "LFLOW-CLASSIFIER", NULL, + nbctl_lflow_classifier_get_logical_destination_port, NULL, "", RO }, + { "lflow-classifier-set-logical-destination-port", 2, 2, "LFLOW-CLASSIFIER LDESTINATION_PORT", NULL, + nbctl_lflow_classifier_set_logical_destination_port, NULL, "", RO }, + /* TODO ADD OTHER FLOW-CLASSIFIER PARAMETERS */ + + /* lsp-chain commands. */ + { "lsp-chain-add", 2, 3, "SWITCH [CHAIN] LAST_PORT", NULL, nbctl_lsp_chain_add, + NULL, "--may-exist,--add-duplicate", RW }, + { "lsp-chain-del", 1, 1, "CHAIN", NULL, nbctl_lsp_chain_del, + NULL, "--if-exists", RW }, + { "lsp-chain-list", 0, 2, "[SWITCH [CHAIN]]", NULL, nbctl_lsp_chain_list, NULL, "", RO }, + { "lsp-chain-show", 0, 1, "[CHAIN]", NULL, nbctl_lsp_chain_show, NULL, "", RO }, + + /* lsp-pair-group commands. */ + { "lsp-pair-group-add", 1, 3, "CHAIN [PAIR-GROUP [OFFSET]]", + NULL, nbctl_lsp_pair_group_add, NULL, "--may-exist,--add-duplicate", RW }, + { "lsp-pair-group-del", 1, 1, "PAIR-GROUP", NULL, nbctl_lsp_pair_group_del, + NULL, "--if-exists", RW }, + { "lsp-pair-group-list", 1, 1, "CHAIN", NULL, nbctl_lsp_pair_group_list, NULL, "", RO }, + { "lsp-pair-group-add-port-pair", 2, 2, "PAIR-GROUP LSP-PAIR", + NULL, nbctl_lsp_pair_group_add_port_pair, NULL, "--may-exist", RW }, + { "lsp-pair-group-del-port-pair", 2, 2, "PAIR-GROUP LSP-PAIR", + NULL, nbctl_lsp_pair_group_del_port_pair, NULL, "--if-exists", RW }, + + /* lsp-pair commands. */ + { "lsp-pair-add", 3, 4, "SWITCH, PORT-IN, PORT-OUT [LSP-PAIR]", NULL, nbctl_lsp_pair_add, + NULL, "--may-exist,--add-duplicate", RW }, + { "lsp-pair-del", 1, 1, "LSP-PAIR", NULL, nbctl_lsp_pair_del, + NULL, "--if-exists", RW }, + { "lsp-pair-list", 0, 2, "[SWITCH [LSP-PAIR]]", NULL, nbctl_lsp_pair_list, NULL, "", RO }, /* logical switch commands. */ { "ls-add", 0, 1, "[SWITCH]", NULL, nbctl_ls_add, NULL, @@ -3284,7 +4937,7 @@ static const struct ctl_command_syntax nbctl_commands[] = { { "ls-list", 0, 0, "", NULL, nbctl_ls_list, NULL, "", RO }, /* acl commands. */ - { "acl-add", 5, 5, "SWITCH DIRECTION PRIORITY MATCH ACTION", NULL, + { "acl-add", 5, 6, "SWITCH DIRECTION PRIORITY MATCH ACTION [ACL-OPTIONS]", NULL, nbctl_acl_add, NULL, "--log", RW }, { "acl-del", 1, 4, "SWITCH [DIRECTION [PRIORITY MATCH]]", NULL, nbctl_acl_del, NULL, "", RW }, -- 2.11.0 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
