}
/* Table 0: Ingress port security. */
- const struct nbrec_logical_port *lport;
- NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
- struct ds match = DS_EMPTY_INITIALIZER;
- ds_put_cstr(&match, "inport == ");
- json_string_escape(lport->name, &match);
- build_port_security("eth.src",
- lport->port_security, lport->n_port_security,
- &match);
- pipeline_add(&pc, lport->lswitch, 0, 50, ds_cstr(&match),
- lport_is_enabled(lport) ? "next;" : "drop;");
- ds_destroy(&match);
- }
-
- /* Table 1: Destination lookup, broadcast and multicast handling (priority
- * 100). */
NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
- struct ds actions;
-
- ds_init(&actions);
- NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
- if (lport->lswitch == lswitch && lport_is_enabled(lport)) {
- ds_put_cstr(&actions, "outport = ");
- json_string_escape(lport->name, &actions);
- ds_put_cstr(&actions, "; next; ");
- }
+ for (size_t i = 0; i < lswitch->n_ports; i++) {
+ const struct nbrec_logical_port *lport = lswitch->ports[i];
+ struct ds match = DS_EMPTY_INITIALIZER;
+ ds_put_cstr(&match, "inport == ");
+ json_string_escape(lport->name, &match);
+ build_port_security("eth.src",
+ lport->port_security, lport->n_port_security,
+ &match);
+ pipeline_add(&pc, lswitch, 0, 50, ds_cstr(&match),
+ lport_is_enabled(lport) ? "next;" : "drop;");
+ ds_destroy(&match);
}
- ds_chomp(&actions, ' ');
-
- pipeline_add(&pc, lswitch, 1, 100, "eth.dst[40]", ds_cstr(&actions));
- ds_destroy(&actions);
}
- /* Table 1: Destination lookup, unicast handling (priority 50), */
- struct unknown_actions {
- struct hmap_node hmap_node;
- const struct nbrec_logical_switch *ls;
- struct ds actions;
- };
- struct hmap unknown_actions = HMAP_INITIALIZER(&unknown_actions);
- NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
- lswitch = lport->lswitch;
- for (size_t i = 0; i < lport->n_macs; i++) {
- uint8_t mac[ETH_ADDR_LEN];
-
- if (eth_addr_from_string(lport->macs[i], mac)) {
- struct ds match, actions;
-
- ds_init(&match);
- ds_put_format(&match, "eth.dst == %s", lport->macs[i]);
-
- ds_init(&actions);
- ds_put_cstr(&actions, "outport = ");
- json_string_escape(lport->name, &actions);
- ds_put_cstr(&actions, "; next;");
- pipeline_add(&pc, lswitch, 1, 50,
- ds_cstr(&match), ds_cstr(&actions));
- ds_destroy(&actions);
- ds_destroy(&match);
- } else if (!strcmp(lport->macs[i], "unknown")) {
- const struct uuid *uuid = &lswitch->header_.uuid;
- struct unknown_actions *ua = NULL;
- struct unknown_actions *iter;
- HMAP_FOR_EACH_WITH_HASH (iter, hmap_node, uuid_hash(uuid),
- &unknown_actions) {
- if (uuid_equals(&iter->ls->header_.uuid, uuid)) {
- ua = iter;
- break;
- }
- }
- if (!ua) {
- ua = xmalloc(sizeof *ua);
- hmap_insert(&unknown_actions, &ua->hmap_node,
- uuid_hash(uuid));
- ua->ls = lswitch;
- ds_init(&ua->actions);
+ /* Table 1: Destination lookup:
+ *
+ * - Broadcast and multicast handling (priority 100).
+ * - Unicast handling (priority 50).
+ * - Unknown unicast address handling (priority 0).
+ * */
+ NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
+ struct ds bcast; /* Actions for broadcast on 'lswitch'. */
+ struct ds unknown; /* Actions for unknown MACs on 'lswitch'. */
+
+ ds_init(&bcast);
+ ds_init(&unknown);
+ for (size_t i = 0; i < lswitch->n_ports; i++) {
+ const struct nbrec_logical_port *lport = lswitch->ports[i];
+
+ ds_put_cstr(&bcast, "outport = ");
+ json_string_escape(lport->name, &bcast);
+ ds_put_cstr(&bcast, "; next; ");
+
+ for (size_t j = 0; j < lport->n_macs; j++) {
+ const char *s = lport->macs[j];
+ uint8_t mac[ETH_ADDR_LEN];
+
+ if (eth_addr_from_string(s, mac)) {
+ struct ds match, unicast;
+
+ ds_init(&match);
+ ds_put_format(&match, "eth.dst == %s", s);
+
+ ds_init(&unicast);
+ ds_put_cstr(&unicast, "outport = ");
+ json_string_escape(lport->name, &unicast);
+ ds_put_cstr(&unicast, "; next;");
+ pipeline_add(&pc, lswitch, 1, 50,
+ ds_cstr(&match), ds_cstr(&unicast));
+ ds_destroy(&unicast);
+ ds_destroy(&match);
+ } else if (!strcmp(s, "unknown")) {
+ ds_put_cstr(&unknown, "outport = ");
+ json_string_escape(lport->name, &unknown);
+ ds_put_cstr(&unknown, "; next; ");
} else {
- ds_put_char(&ua->actions, ' ');
- }
-
- ds_put_cstr(&ua->actions, "outport = ");
- json_string_escape(lport->name, &ua->actions);
- ds_put_cstr(&ua->actions, "; next;");
- } else {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_INFO_RL(&rl, "%s: invalid syntax '%s' in macs column",
- lport->name, lport->macs[i]);
+ VLOG_INFO_RL(&rl, "%s: invalid syntax '%s' in macs column",
+ lport->name, s);
+ }
}
}
- }
- /* Table 1: Destination lookup for unknown MACs (priority 0). */
- struct unknown_actions *ua, *next_ua;
- HMAP_FOR_EACH_SAFE (ua, next_ua, hmap_node, &unknown_actions) {
- pipeline_add(&pc, ua->ls, 1, 0, "1", ds_cstr(&ua->actions));
- hmap_remove(&unknown_actions, &ua->hmap_node);
- ds_destroy(&ua->actions);
- free(ua);
+ ds_chomp(&bcast, ' ');
+ pipeline_add(&pc, lswitch, 1, 100, "eth.dst[40]", ds_cstr(&bcast));
+ ds_destroy(&bcast);
+
+ if (unknown.length) {
+ ds_chomp(&unknown, ' ');
+ pipeline_add(&pc, lswitch, 1, 0, "1", ds_cstr(&unknown));
+ }
+ ds_destroy(&unknown);
}
- hmap_destroy(&unknown_actions);
/* Table 2: ACLs. */
- const struct nbrec_acl *acl;
- NBREC_ACL_FOR_EACH (acl, ctx->ovnnb_idl) {
- const char *action;
-
- action = (!strcmp(acl->action, "allow") ||
- !strcmp(acl->action, "allow-related"))
- ? "next;" : "drop;";
- pipeline_add(&pc, acl->lswitch, 2, acl->priority, acl->match, action);
- }
NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
+ for (size_t i = 0; i < lswitch->n_acls; i++) {
+ const struct nbrec_acl *acl = lswitch->acls[i];
+
+ NBREC_ACL_FOR_EACH (acl, ctx->ovnnb_idl) {
+ pipeline_add(&pc, lswitch, 2, acl->priority, acl->match,
+ (!strcmp(acl->action, "allow") ||
+ !strcmp(acl->action, "allow-related")
+ ? "next;" : "drop;"));
+ }
+ }
+
pipeline_add(&pc, lswitch, 2, 0, "1", "next;");
}
/* Table 3: Egress port security. */
NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
pipeline_add(&pc, lswitch, 3, 100, "eth.dst[40]", "output;");
- }
- NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
- struct ds match;
- ds_init(&match);
- ds_put_cstr(&match, "outport == ");
- json_string_escape(lport->name, &match);
- build_port_security("eth.dst",
- lport->port_security, lport->n_port_security,
- &match);
+ for (size_t i = 0; i < lswitch->n_ports; i++) {
+ const struct nbrec_logical_port *lport = lswitch->ports[i];
+ struct ds match;
- pipeline_add(&pc, lport->lswitch, 3, 50, ds_cstr(&match),
- lport_is_enabled(lport) ? "output;" : "drop;");
+ ds_init(&match);
+ ds_put_cstr(&match, "outport == ");
+ json_string_escape(lport->name, &match);
+ build_port_security("eth.dst",
+ lport->port_security, lport->n_port_security,
+ &match);
- ds_destroy(&match);
+ pipeline_add(&pc, lswitch, 3, 50, ds_cstr(&match),
+ lport_is_enabled(lport) ? "output;" : "drop;");
+
+ ds_destroy(&match);
+ }
}
/* Delete any existing Pipeline rows that were not re-generated. */
set_bindings(struct northd_context *ctx)
{
const struct sbrec_binding *binding;
- const struct nbrec_logical_port *lport;
/*
* We will need to look up a binding for every logical port. We don't want
hash_int(binding->tunnel_key, 0));
}
- NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
- struct binding_hash_node *hash_node;
- binding = NULL;
- HMAP_FOR_EACH_WITH_HASH(hash_node, lp_node,
- hash_string(lport->name, 0), &lp_hmap) {
- if (!strcmp(lport->name, hash_node->binding->logical_port)) {
- binding = hash_node->binding;
- break;
+ const struct nbrec_logical_switch *lswitch;
+ NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
+ const struct uuid *logical_datapath = &lswitch->header_.uuid;
+
+ for (size_t i = 0; i < lswitch->n_ports; i++) {
+ const struct nbrec_logical_port *lport = lswitch->ports[i];
+ struct binding_hash_node *hash_node;
+ binding = NULL;
+ HMAP_FOR_EACH_WITH_HASH(hash_node, lp_node,
+ hash_string(lport->name, 0), &lp_hmap) {
+ if (!strcmp(lport->name, hash_node->binding->logical_port)) {
+ binding = hash_node->binding;
+ break;
+ }
}
- }
- struct uuid logical_datapath;
- if (lport->lswitch) {
- logical_datapath = lport->lswitch->header_.uuid;
- } else {
- uuid_zero(&logical_datapath);
- }
+ if (binding) {
+ /* We found an existing binding for this logical port. Update
+ * its contents. */
- if (binding) {
- /* We found an existing binding for this logical port. Update its
- * contents. */
+ hmap_remove(&lp_hmap, &hash_node->lp_node);
- hmap_remove(&lp_hmap, &hash_node->lp_node);
+ if (!macs_equal(binding->mac, binding->n_mac,
+ lport->macs, lport->n_macs)) {
+ sbrec_binding_set_mac(binding, (const char **) lport->macs,
+ lport->n_macs);
+ }
+ if (!parents_equal(binding, lport)) {
+ sbrec_binding_set_parent_port(binding, lport->parent_name);
+ }
+ if (!tags_equal(binding, lport)) {
+ sbrec_binding_set_tag(binding, lport->tag, lport->n_tag);
+ }
+ if (!uuid_equals(&binding->logical_datapath,
+ logical_datapath)) {
+ sbrec_binding_set_logical_datapath(binding,
+ *logical_datapath);
+ }
+ } else {
+ /* There is no binding for this logical port, so create one. */
- if (!macs_equal(binding->mac, binding->n_mac,
- lport->macs, lport->n_macs)) {
- sbrec_binding_set_mac(binding,
- (const char **) lport->macs, lport->n_macs);
- }
- if (!parents_equal(binding, lport)) {
- sbrec_binding_set_parent_port(binding, lport->parent_name);
- }
- if (!tags_equal(binding, lport)) {
- sbrec_binding_set_tag(binding, lport->tag, lport->n_tag);
- }
- if (!uuid_equals(&binding->logical_datapath, &logical_datapath)) {
- sbrec_binding_set_logical_datapath(binding,
- logical_datapath);
- }
- } else {
- /* There is no binding for this logical port, so create one. */
+ uint16_t tunnel_key = choose_tunnel_key(&tk_hmap);
+ if (!tunnel_key) {
+ continue;
+ }
- uint16_t tunnel_key = choose_tunnel_key(&tk_hmap);
- if (!tunnel_key) {
- continue;
- }
+ binding = sbrec_binding_insert(ctx->ovnsb_txn);
+ sbrec_binding_set_logical_port(binding, lport->name);
+ sbrec_binding_set_mac(binding, (const char **) lport->macs,
+ lport->n_macs);
+ if (lport->parent_name && lport->n_tag > 0) {
+ sbrec_binding_set_parent_port(binding, lport->parent_name);
+ sbrec_binding_set_tag(binding, lport->tag, lport->n_tag);
+ }
- binding = sbrec_binding_insert(ctx->ovnsb_txn);
- sbrec_binding_set_logical_port(binding, lport->name);
- sbrec_binding_set_mac(binding,
- (const char **) lport->macs, lport->n_macs);
- if (lport->parent_name && lport->n_tag > 0) {
- sbrec_binding_set_parent_port(binding, lport->parent_name);
- sbrec_binding_set_tag(binding, lport->tag, lport->n_tag);
+ sbrec_binding_set_tunnel_key(binding, tunnel_key);
+ sbrec_binding_set_logical_datapath(binding, *logical_datapath);
+
+ /* Add the tunnel key to the tk_hmap so that we don't try to
+ * use it for another port. (We don't want it in the lp_hmap
+ * because that would just get the Binding record deleted
+ * later.) */
+ struct binding_hash_node *hash_node
+ = xzalloc(sizeof *hash_node);
+ hash_node->binding = binding;
+ hmap_insert(&tk_hmap, &hash_node->tk_node,
+ hash_int(binding->tunnel_key, 0));
}
-
- sbrec_binding_set_tunnel_key(binding, tunnel_key);
- sbrec_binding_set_logical_datapath(binding, logical_datapath);
-
- /* Add the tunnel key to the tk_hmap so that we don't try to use it
- * for another port. (We don't want it in the lp_hmap because that
- * would just get the Binding record deleted later.) */
- struct binding_hash_node *hash_node = xzalloc(sizeof *hash_node);
- hash_node->binding = binding;
- hmap_insert(&tk_hmap, &hash_node->tk_node,
- hash_int(binding->tunnel_key, 0));
}
}
"Logical_Switch": {
"columns": {
"name": {"type": "string"},
+ "ports": {"type": {"key": {"type": "uuid",
+ "refTable": "Logical_Port",
+ "refType": "strong"},
+ "min": 0,
+ "max": "unlimited"}},
+ "acls": {"type": {"key": {"type": "uuid",
+ "refTable": "ACL",
+ "refType": "strong"},
+ "min": 0,
+ "max": "unlimited"}},
"router_port": {"type": {"key": {"type": "uuid",
"refTable": "Logical_Router_Port",
"refType": "strong"},
"min": 0, "max": 1}},
"external_ids": {
"type": {"key": "string", "value": "string",
- "min": 0, "max": "unlimited"}}}},
+ "min": 0, "max": "unlimited"}}},
+ "isRoot": true},
"Logical_Port": {
"columns": {
- "lswitch": {"type": {"key": {"type": "uuid",
- "refTable": "Logical_Switch",
- "refType": "strong"}}},
"name": {"type": "string"},
"parent_name": {"type": {"key": "string", "min": 0, "max": 1}},
"tag": {
"external_ids": {
"type": {"key": "string", "value": "string",
"min": 0, "max": "unlimited"}}},
- "indexes": [["name"]]},
+ "indexes": [["name"]],
+ "isRoot": false},
"ACL": {
"columns": {
- "lswitch": {"type": {"key": {"type": "uuid",
- "refTable": "Logical_Switch",
- "refType": "strong"}}},
"priority": {"type": {"key": {"type": "integer",
"minInteger": 1,
"maxInteger": 65535}}},
"log": {"type": "boolean"},
"external_ids": {
"type": {"key": "string", "value": "string",
- "min": 0, "max": "unlimited"}}}},
+ "min": 0, "max": "unlimited"}}},
+ "isRoot": false},
"Logical_Router": {
"columns": {
+ "ports": {"type": {"key": {"type": "uuid",
+ "refTable": "Logical_Router_Port",
+ "refType": "weak"},
+ "min": 0,
+ "max": "unlimited"}},
"ip": {"type": "string"},
"default_gw": {"type": {"key": "string", "min": 0, "max": 1}},
"external_ids": {
"type": {"key": "string", "value": "string",
- "min": 0, "max": "unlimited"}}}},
+ "min": 0, "max": "unlimited"}}},
+ "isRoot": true},
"Logical_Router_Port": {
"columns": {
- "router": {"type": {"key": {"type": "uuid",
- "refTable": "Logical_Router",
- "refType": "strong"}}},
"network": {"type": "string"},
"mac": {"type": "string"},
"external_ids": {
"type": {"key": "string", "value": "string",
- "min": 0, "max": "unlimited"}}}}},
+ "min": 0, "max": "unlimited"}}},
+ "isRoot": false}},
"version": "1.0.0"}
<table name="Logical_Switch" title="L2 logical switch">
<p>
- Each row represents one L2 logical switch. A given switch's ports are
- the <ref table="Logical_Port"/> rows whose <ref table="Logical_Port"
- column="lswitch"/> column points to its row.
+ Each row represents one L2 logical switch.
</p>
<column name="name">
</p>
</column>
+ <column name="ports">
+ <p>
+ The logical ports 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="router_port">
<p>
The router port to which this logical switch is connected, or empty if
restriction because logical routers may be connected into arbitrary
topologies.
</p>
+
+ <p>
+ It is an error for multiple logical switches to refer to the same
+ router port.
+ </p>
+ </column>
+
+ <column name="acls">
+ Access control rules that apply to packets within the logical switch.
</column>
<group title="Common Columns">
A port within an L2 logical switch.
</p>
- <column name="lswitch">
- The logical switch to which the logical port is connected.
- </column>
-
<column name="name">
<p>
The logical port name.
<table name="ACL" title="Access Control List (ACL) rule">
<p>
- Each row in this table represents one ACL rule for the logical switch in
- its <ref column="lswitch"/> column. The <ref column="action"/> column for
- the highest-<ref column="priority"/> matching row in this table
- determines a packet's treatment. If no row matches, packets are allowed
- by default. (Default-deny treatment is possible: add a rule with <ref
- column="priority"/> 1, <code>1</code> as <ref column="match"/>, and
- <code>deny</code> as <ref column="action"/>.)
+ Each row in this table represents one ACL rule for a logical switch
+ that points to it through its <ref column="acls"/> column. The <ref
+ column="action"/> column for the highest-<ref column="priority"/>
+ matching row in this table determines a packet's treatment. If no row
+ matches, packets are allowed by default. (Default-deny treatment is
+ possible: add a rule with <ref column="priority"/> 1, <code>1</code> as
+ <ref column="match"/>, and <code>deny</code> as <ref column="action"/>.)
</p>
- <column name="lswitch">
- The switch to which the ACL rule applies. The expression in the
- <ref column="match"/> column may match against logical ports
- within this switch.
- </column>
-
<column name="priority">
The ACL rule's priority. Rules with numerically higher priority take
precedence over those with lower. If two ACL rules with the same
<table name="Logical_Router" title="L3 logical router">
<p>
- Each row represents one L3 logical router. A given router's ports are
- the <ref table="Logical_Router_Port"/> rows whose <ref
- table="Logical_Router_Port" column="router"/> column points to its row.
+ Each row represents one L3 logical router.
</p>
+ <column name="ports">
+ The router's ports. This is a set of weak references, so a <ref
+ table="Logical_Switch"/> must also refer to any given <ref
+ table="Logical_Router_Port"/> or it will automatically be deleted.
+ </column>
+
<column name="ip">
The logical router's own IP address. The logical router uses this
address for ICMP replies (e.g. network unreachable messages) and other
</p>
<p>
- A router port is always attached to a switch port. The connection can be
- identified by following the <ref column="router_port"
- table="Logical_Port"/> column from an appropriate <ref
- table="Logical_Port"/> row.
+ A router port is always attached to a logical switch and to a logical
+ router. The former attachment, which is enforced by the database schema,
+ can be identified by finding the <ref table="Logical_Switch"/> row whose
+ <ref column="router_port" table="Logical_Switch"/> column points to the
+ router port. The latter attachment, which the database schema does not
+ enforce, can be identified by finding the <ref table="Logical_Router"/>
+ row whose <ref column="ports" table="Logical_Router"/> column includes
+ the router port.
</p>
- <column name="router">
- The router to which the port belongs.
- </column>
-
<column name="network">
The IP network and netmask of the network on the router port. Used for
routing.
}
static void
-print_lswitch(const struct nbctl_context *nb_ctx,
- const struct nbrec_logical_switch *lswitch)
+print_lswitch(const struct nbrec_logical_switch *lswitch)
{
- const struct nbrec_logical_port *lport;
-
printf(" lswitch "UUID_FMT" (%s)\n",
UUID_ARGS(&lswitch->header_.uuid), lswitch->name);
- NBREC_LOGICAL_PORT_FOR_EACH(lport, nb_ctx->idl) {
- int i;
+ for (size_t i = 0; i < lswitch->n_ports; i++) {
+ const struct nbrec_logical_port *lport = lswitch->ports[i];
- if (lport->lswitch == lswitch) {
- printf(" lport %s\n", lport->name);
- if (lport->parent_name && lport->n_tag) {
- printf(" parent: %s, tag:%"PRIu64"\n",
- lport->parent_name, lport->tag[0]);
- }
- if (lport->n_macs) {
- printf(" macs:");
- for (i=0; i < lport->n_macs; i++) {
- printf(" %s", lport->macs[i]);
- }
- printf("\n");
+ printf(" lport %s\n", lport->name);
+ if (lport->parent_name && lport->n_tag) {
+ printf(" parent: %s, tag:%"PRIu64"\n",
+ lport->parent_name, lport->tag[0]);
+ }
+ if (lport->n_macs) {
+ printf(" macs:");
+ for (size_t j = 0; j < lport->n_macs; j++) {
+ printf(" %s", lport->macs[j]);
}
+ printf("\n");
}
}
}
if (ctx->argc == 2) {
lswitch = lswitch_by_name_or_uuid(nb_ctx, ctx->argv[1]);
if (lswitch) {
- print_lswitch(nb_ctx, lswitch);
+ print_lswitch(lswitch);
}
} else {
NBREC_LOGICAL_SWITCH_FOR_EACH(lswitch, nb_ctx->idl) {
- print_lswitch(nb_ctx, lswitch);
+ print_lswitch(lswitch);
}
}
}
}
if (ctx->argc != 3 && ctx->argc != 5) {
- /* If a parent_name is specififed, a tag must be specified as well. */
+ /* If a parent_name is specified, a tag must be specified as well. */
VLOG_WARN("Invalid arguments to lport-add.");
return;
}
}
}
- /* Finally, create the transaction. */
+ /* Create the logical port. */
lport = nbrec_logical_port_insert(nb_ctx->txn);
nbrec_logical_port_set_name(lport, ctx->argv[2]);
- nbrec_logical_port_set_lswitch(lport, lswitch);
if (ctx->argc == 5) {
nbrec_logical_port_set_parent_name(lport, ctx->argv[3]);
nbrec_logical_port_set_tag(lport, &tag, 1);
}
+
+ /* Insert the logical port into the logical switch. */
+ nbrec_logical_switch_verify_ports(lswitch);
+ struct nbrec_logical_port **new_ports = xmalloc(sizeof *new_ports *
+ (lswitch->n_ports + 1));
+ memcpy(new_ports, lswitch->ports, sizeof *new_ports * lswitch->n_ports);
+ new_ports[lswitch->n_ports] = lport;
+ nbrec_logical_switch_set_ports(lswitch, new_ports, lswitch->n_ports + 1);
+ free(new_ports);
+}
+
+/* Removes lport 'lswitch->ports[idx]'. */
+static void
+remove_lport(const struct nbrec_logical_switch *lswitch, size_t idx)
+{
+ const struct nbrec_logical_port *lport = lswitch->ports[idx];
+
+ /* First remove 'lport' from the array of ports. This is what will
+ * actually cause the logical port to be deleted when the transaction is
+ * sent to the database server (due to garbage collection). */
+ struct nbrec_logical_port **new_ports
+ = xmemdup(lswitch->ports, sizeof *new_ports * lswitch->n_ports);
+ new_ports[idx] = new_ports[lswitch->n_ports - 1];
+ nbrec_logical_switch_verify_ports(lswitch);
+ nbrec_logical_switch_set_ports(lswitch, new_ports, lswitch->n_ports - 1);
+ free(new_ports);
+
+ /* Delete 'lport' 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_FOR_EACH later. */
+ nbrec_logical_port_delete(lport);
}
static void
return;
}
- nbrec_logical_port_delete(lport);
-}
-
-static bool
-is_lswitch(const struct nbrec_logical_switch *lswitch,
- struct uuid *lswitch_uuid, const char *name)
-{
- if (lswitch_uuid) {
- return uuid_equals(lswitch_uuid, &lswitch->header_.uuid);
- } else {
- return !strcmp(lswitch->name, name);
+ /* Find the switch that contains 'lport', then delete it. */
+ const struct nbrec_logical_switch *lswitch;
+ NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, nb_ctx->idl) {
+ for (size_t i = 0; i < lswitch->n_ports; i++) {
+ if (lswitch->ports[i] == lport) {
+ remove_lport(lswitch, i);
+ return;
+ }
+ }
}
-}
+ VLOG_WARN("logical port %s is not part of any logical switch",
+ ctx->argv[1]);
+}
static void
do_lport_list(struct ovs_cmdl_context *ctx)
{
struct nbctl_context *nb_ctx = ctx->pvt;
const char *id = ctx->argv[1];
- const struct nbrec_logical_port *lport;
- bool is_uuid = false;
- struct uuid lswitch_uuid;
+ const struct nbrec_logical_switch *lswitch;
- if (uuid_from_string(&lswitch_uuid, id)) {
- is_uuid = true;
+ lswitch = lswitch_by_name_or_uuid(nb_ctx, id);
+ if (!lswitch) {
+ return;
}
- NBREC_LOGICAL_PORT_FOR_EACH(lport, nb_ctx->idl) {
- bool match;
- if (is_uuid) {
- match = is_lswitch(lport->lswitch, &lswitch_uuid, NULL);
- } else {
- match = is_lswitch(lport->lswitch, NULL, id);
- }
- if (!match) {
- continue;
- }
+ for (size_t i = 0; i < lswitch->n_ports; i++) {
+ const struct nbrec_logical_port *lport = lswitch->ports[i];
printf(UUID_FMT " (%s)\n",
UUID_ARGS(&lport->header_.uuid), lport->name);
}