ovn-nbctl provides a shortcut to perform commands related lswitch, lport and such but it doesn't have similar commands related to logical router and logical router port. Also, 'nbctl showi' is supposed to show an overview of database contents, which means it should show the routers as well. "show LSWITCH" shows the switch details, similarly "show LROUTER" should show the router details too. This patch takes care of all of these. Unit test cases have been added to test all of these modifications and additions.
Modifications; 1) ovn-nbctl show -- will now show lrouters as well 2) ovn-nbctl show <lrouter> -- will show the router now New commands added: 3) ovn-nbctl lrouter-add [LROUTER] 4) ovn-nbctl lrouter-del LROUTER 5) ovn-nbctl lrouter-list 6) lrport-add LROUTER LRPORT 7) lrport-del LRPORT 8) lrport-list LROUTER 9) lrport-set-mac-address LRPORT [ADDRESS] 10) lrport-get-mac-address LRPORT 11) lrport-set-enabled LRPORT STATE 12) lrport-get-enabled LRPORT Author: Nirapada Ghosh <ngh...@us.ibm.com> Date: Fri, 8 Apr 2016 18:44:21 -0700 Signed-off-by: Nirapada Ghosh <ngh...@us.ibm.com> --- diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c index bdad368..6d18005 100644 --- a/ovn/utilities/ovn-nbctl.c +++ b/ovn/utilities/ovn-nbctl.c @@ -290,12 +290,18 @@ usage: %s [OPTIONS] COMMAND [ARG...]\n\ General commands:\n\ show print overview of database contents\n\ show LSWITCH print overview of database contents for LSWITCH\n\ + show LROUTER print overview of database contents for LROUTER\n\ \n\ Logical switch commands:\n\ lswitch-add [LSWITCH] create a logical switch named LSWITCH\n\ lswitch-del LSWITCH delete LSWITCH and all its ports\n\ lswitch-list print the names of all logical switches\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\ ACL commands:\n\ acl-add LSWITCH DIRECTION PRIORITY MATCH ACTION [log]\n\ add an ACL to LSWITCH\n\ @@ -303,6 +309,19 @@ ACL commands:\n\ remove ACLs from LSWITCH\n\ acl-list LSWITCH print ACLs for LSWITCH\n\ \n\ +Logical router port commands:\n\ + lrport-add LROUTER LRPORT add logical router port LRPORT to LROUTER\n\ + lrport-del LRPORT delete LRPORT from its attached router\n\ + lrport-list LROUTER print the names of all logical ports on LROUTER\n\ + lrport-set-mac-address LRPORT [ADDRESS]\n\ + set MAC address for LRPORT.\n\ + lrport-get-mac-address LRPORT get MAC addresses on LRPORT\n\ + lrport-set-enabled LRPORT STATE\n\ + set administrative state LRPORT\n\ + ('enabled' or 'disabled')\n\ + lrport-get-enabled LRPORT get administrative state LRPORT\n\ + ('enabled' or 'disabled')\n\ +\n\ Logical port commands:\n\ lport-add LSWITCH LPORT add logical port LPORT on LSWITCH\n\ lport-add LSWITCH LPORT PARENT TAG\n\ @@ -349,8 +368,49 @@ Other options:\n\ exit(EXIT_SUCCESS); } + +/* Following function finds out the lrouter given it's id. */ +static const struct nbrec_logical_router * +lrouter_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool silent) +{ + const struct nbrec_logical_router *lrouter = NULL; + bool is_uuid = false; + bool duplicate = false; + struct uuid lrouter_uuid; + + if (uuid_from_string(&lrouter_uuid, id)) { + is_uuid = true; + lrouter = nbrec_logical_router_get_for_uuid(ctx->idl, + &lrouter_uuid); + } + + if (!lrouter) { + const struct nbrec_logical_router *iter; + + NBREC_LOGICAL_ROUTER_FOR_EACH(iter, ctx->idl) { + if (strcmp(iter->name, id)) { + continue; + } + if (lrouter) { + VLOG_WARN("There is more than one logical router named '%s'. " + "Use a UUID.", id); + lrouter = NULL; + duplicate = true; + break; + } + lrouter = iter; + } + } + + if (!lrouter && !duplicate && !silent) { + VLOG_WARN("lrouter not found for %s: '%s'", + is_uuid ? "UUID" : "name", id); + } + + return lrouter; +} static const struct nbrec_logical_switch * -lswitch_by_name_or_uuid(struct ctl_context *ctx, const char *id) +lswitch_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool silent) { const struct nbrec_logical_switch *lswitch = NULL; bool is_uuid = false; @@ -381,7 +441,7 @@ lswitch_by_name_or_uuid(struct ctl_context *ctx, const char *id) } } - if (!lswitch && !duplicate) { + if (!lswitch && !duplicate && !silent) { VLOG_WARN("lswitch not found for %s: '%s'", is_uuid ? "UUID" : "name", id); } @@ -389,6 +449,24 @@ lswitch_by_name_or_uuid(struct ctl_context *ctx, const char *id) return lswitch; } +/* Given pointer to lrouter, this routine prints the lrouter information. */ +static void +print_lrouter(const struct nbrec_logical_router *lrouter, struct ds *s) +{ + ds_put_format(s, " lrouter "UUID_FMT" (%s)\n", + UUID_ARGS(&lrouter->header_.uuid), lrouter->name); + + for (size_t i = 0; i < lrouter->n_ports; i++) { + const struct nbrec_logical_router_port *lrport = lrouter->ports[i]; + ds_put_format(s, " lrport %s\n", lrport->name); + if (lrport->mac) { + ds_put_cstr(s, " mac: "); + ds_put_format(s, "\"%s\"", lrport->mac); + } + ds_put_format(s, "\n"); + } +} + static void print_lswitch(const struct nbrec_logical_switch *lswitch, struct ds *s) { @@ -423,7 +501,7 @@ nbctl_show(struct ctl_context *ctx) const struct nbrec_logical_switch *lswitch; if (ctx->argc == 2) { - lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1]); + lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1], true); if (lswitch) { print_lswitch(lswitch, &ctx->output); } @@ -432,6 +510,66 @@ nbctl_show(struct ctl_context *ctx) print_lswitch(lswitch, &ctx->output); } } + const struct nbrec_logical_router *lrouter; + + if (ctx->argc == 2) { + lrouter = lrouter_by_name_or_uuid(ctx, ctx->argv[1], true); + if (lrouter) { + print_lrouter(lrouter, &ctx->output); + } + } else { + NBREC_LOGICAL_ROUTER_FOR_EACH(lrouter, ctx->idl) { + print_lrouter(lrouter, &ctx->output); + } + } +} + +/* Following function adds a lrouter to NB */ +static void +nbctl_lrouter_add(struct ctl_context *ctx) +{ + struct nbrec_logical_router *lrouter; + + lrouter = nbrec_logical_router_insert(ctx->txn); + if (ctx->argc == 2) { + nbrec_logical_router_set_name(lrouter, ctx->argv[1]); + } +} + +/* Following function deletes a lrouter from NB */ +static void +nbctl_lrouter_del(struct ctl_context *ctx) +{ + const char *id = ctx->argv[1]; + const struct nbrec_logical_router *lrouter; + + lrouter = lrouter_by_name_or_uuid(ctx, id, false); + if (!lrouter) { + return; + } + + nbrec_logical_router_delete(lrouter); +} + +/* Following function prints list of lrouters in NB */ +static void +nbctl_lrouter_list(struct ctl_context *ctx) +{ + const struct nbrec_logical_router *lrouter; + struct smap lrouters; + + smap_init(&lrouters); + NBREC_LOGICAL_ROUTER_FOR_EACH(lrouter, ctx->idl) { + smap_add_format(&lrouters, lrouter->name, UUID_FMT " (%s)", + UUID_ARGS(&lrouter->header_.uuid), lrouter->name); + } + const struct smap_node **nodes = smap_sort(&lrouters); + for (size_t i = 0; i < smap_count(&lrouters); i++) { + const struct smap_node *node = nodes[i]; + ds_put_format(&ctx->output, "%s\n", node->value); + } + smap_destroy(&lrouters); + free(nodes); } static void @@ -451,7 +589,7 @@ nbctl_lswitch_del(struct ctl_context *ctx) const char *id = ctx->argv[1]; const struct nbrec_logical_switch *lswitch; - lswitch = lswitch_by_name_or_uuid(ctx, id); + lswitch = lswitch_by_name_or_uuid(ctx, id, false); if (!lswitch) { return; } @@ -479,6 +617,37 @@ nbctl_lswitch_list(struct ctl_context *ctx) free(nodes); } + +/* Following function finds out the lrport given it's id. */ +static const struct nbrec_logical_router_port * +lrport_by_name_or_uuid(struct ctl_context *ctx, const char *id) +{ + const struct nbrec_logical_router_port *lrport = NULL; + bool is_uuid = false; + struct uuid lrport_uuid; + + if (uuid_from_string(&lrport_uuid, id)) { + is_uuid = true; + lrport = nbrec_logical_router_port_get_for_uuid(ctx->idl, + &lrport_uuid); + } + + if (!lrport) { + NBREC_LOGICAL_ROUTER_PORT_FOR_EACH(lrport, ctx->idl) { + if (!strcmp(lrport->name, id)) { + break; + } + } + } + + if (!lrport) { + VLOG_WARN("lrport not found for %s: '%s'", + is_uuid ? "UUID" : "name", id); + } + + return lrport; +} + static const struct nbrec_logical_port * lport_by_name_or_uuid(struct ctl_context *ctx, const char *id) { @@ -514,7 +683,7 @@ nbctl_lport_add(struct ctl_context *ctx) const struct nbrec_logical_switch *lswitch; int64_t tag; - lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1]); + lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1], false); if (!lswitch) { return; } @@ -551,6 +720,26 @@ nbctl_lport_add(struct ctl_context *ctx) free(new_ports); } +/* Removes lrport 'lrouter->ports[idx]' from lrouter. */ +static void +remove_lrport(const struct nbrec_logical_router *lrouter, size_t idx) +{ + const struct nbrec_logical_router_port *lrport = lrouter->ports[idx]; + + /* First remove 'lrport' 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_router_port **new_ports + = xmemdup(lrouter->ports, sizeof *new_ports * lrouter->n_ports); + new_ports[idx] = new_ports[lrouter->n_ports - 1]; + nbrec_logical_router_verify_ports(lrouter); + nbrec_logical_router_set_ports(lrouter, new_ports, lrouter->n_ports - 1); + free(new_ports); + + /* Delete 'lrport' from the IDL. */ + nbrec_logical_router_port_delete(lrport); +} + /* Removes lport 'lswitch->ports[idx]'. */ static void remove_lport(const struct nbrec_logical_switch *lswitch, size_t idx) @@ -606,7 +795,7 @@ nbctl_lport_list(struct ctl_context *ctx) struct smap lports; size_t i; - lswitch = lswitch_by_name_or_uuid(ctx, id); + lswitch = lswitch_by_name_or_uuid(ctx, id, false); if (!lswitch) { return; } @@ -656,6 +845,32 @@ nbctl_lport_get_tag(struct ctl_context *ctx) } } +/* Following function sets the mac address of lrport. */ +static void +nbctl_lrport_set_mac(struct ctl_context *ctx) +{ + struct eth_addr ea; + const char *id = ctx->argv[1]; + const struct nbrec_logical_router_port *lrport; + + lrport = lrport_by_name_or_uuid(ctx, id); + if (!lrport) { + return; + } + if (ctx->argc != 3) { + VLOG_ERR("Invalid usage"); + return; + } + + if (strcmp(ctx->argv[2], "unknown") + && !ovs_scan(ctx->argv[2], ETH_ADDR_SCAN_FMT, + ETH_ADDR_SCAN_ARGS(ea))) { + VLOG_ERR("Invalid MAC address format, See ovn-nb(5)"); + return; + } + + nbrec_logical_router_port_set_mac(lrport, ctx->argv[2]); +} static void nbctl_lport_set_addresses(struct ctl_context *ctx) { @@ -686,6 +901,20 @@ nbctl_lport_set_addresses(struct ctl_context *ctx) (const char **) ctx->argv + 2, ctx->argc - 2); } +/* Following function prints the mac address of the lrport. */ +static void +nbctl_lrport_get_mac(struct ctl_context *ctx) +{ + const char *id = ctx->argv[1]; + const struct nbrec_logical_router_port *lrport; + + lrport = lrport_by_name_or_uuid(ctx, id); + if (!lrport) { + return; + } + ds_put_format(&ctx->output, "%s\n", lrport->mac); +} + static void nbctl_lport_get_addresses(struct ctl_context *ctx) { @@ -766,6 +995,29 @@ nbctl_lport_get_up(struct ctl_context *ctx) "%s\n", (lport->up && *lport->up) ? "up" : "down"); } +/* Following function sets the lrport to admin-enabled. */ +static void +nbctl_lrport_set_enabled(struct ctl_context *ctx) +{ + const char *id = ctx->argv[1]; + const char *state = ctx->argv[2]; + const struct nbrec_logical_router_port *lrport; + + lrport = lrport_by_name_or_uuid(ctx, id); + if (!lrport) { + return; + } + + if (!strcasecmp(state, "enabled")) { + bool enabled = true; + nbrec_logical_router_port_set_enabled(lrport, &enabled, 1); + } else if (!strcasecmp(state, "disabled")) { + bool enabled = false; + nbrec_logical_router_port_set_enabled(lrport, &enabled, 1); + } else { + VLOG_ERR("Invalid state '%s' provided to lport-set-enabled", state); + } +} static void nbctl_lport_set_enabled(struct ctl_context *ctx) { @@ -789,6 +1041,22 @@ nbctl_lport_set_enabled(struct ctl_context *ctx) } } +/* Following function prints admin-enabled state. */ +static void +nbctl_lrport_get_enabled(struct ctl_context *ctx) +{ + const char *id = ctx->argv[1]; + const struct nbrec_logical_router_port *lrport; + + lrport = lrport_by_name_or_uuid(ctx, id); + if (!lrport) { + return; + } + + ds_put_format(&ctx->output, "%s\n", + !lrport->enabled || + *lrport->enabled ? "enabled" : "disabled"); +} static void nbctl_lport_get_enabled(struct ctl_context *ctx) { @@ -922,7 +1190,7 @@ nbctl_acl_list(struct ctl_context *ctx) const struct nbrec_acl **acls; size_t i; - lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1]); + lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1], false); if (!lswitch) { return; } @@ -951,7 +1219,7 @@ nbctl_acl_add(struct ctl_context *ctx) const char *direction; int64_t priority; - lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1]); + lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1], false); if (!lswitch) { return; } @@ -1007,7 +1275,7 @@ nbctl_acl_del(struct ctl_context *ctx) const char *direction; int64_t priority = 0; - lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1]); + lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1], false); if (!lswitch) { return; } @@ -1302,6 +1570,92 @@ nbctl_exit(int status) exit(status); } +/* Following function prints the list of lrports. */ +static void +nbctl_lrport_list(struct ctl_context *ctx) +{ + const char *id = ctx->argv[1]; + const struct nbrec_logical_router *lrouter; + struct smap lports; + size_t i; + + lrouter = lrouter_by_name_or_uuid(ctx, id, false); + if (!lrouter) { + return; + } + + smap_init(&lports); + for (i = 0; i < lrouter->n_ports; i++) { + const struct nbrec_logical_router_port *lport = lrouter->ports[i]; + smap_add_format(&lports, lport->name, UUID_FMT " (%s)", + UUID_ARGS(&lport->header_.uuid), lport->name); + } + const struct smap_node **nodes = smap_sort(&lports); + for (i = 0; i < smap_count(&lports); i++) { + const struct smap_node *node = nodes[i]; + ds_put_format(&ctx->output, "%s\n", node->value); + } + smap_destroy(&lports); + free(nodes); +} + +/* Following function adds an lrport to the lrouter. */ +static void +nbctl_lrport_add(struct ctl_context *ctx) +{ + struct nbrec_logical_router_port *lport; + const struct nbrec_logical_router *lrouter; + + lrouter = lrouter_by_name_or_uuid(ctx, ctx->argv[1], false); + if (!lrouter) { + return; + } + + if (ctx->argc != 3) { + VLOG_WARN("Invalid arguments to lrport-add."); + return; + } + + /* Create the logical router port. */ + lport = nbrec_logical_router_port_insert(ctx->txn); + nbrec_logical_router_port_set_name(lport, ctx->argv[2]); + + /* Insert the logical port into the logical router. */ + nbrec_logical_router_verify_ports(lrouter); + struct nbrec_logical_router_port **new_ports = xmalloc(sizeof *new_ports * + (lrouter->n_ports + 1)); + memcpy(new_ports, lrouter->ports, sizeof *new_ports * lrouter->n_ports); + new_ports[lrouter->n_ports] = lport; + nbrec_logical_router_set_ports(lrouter, new_ports, lrouter->n_ports + 1); + free(new_ports); +} + +/* Following function deletes the lrport from lrouter. */ +static void +nbctl_lrport_del(struct ctl_context *ctx) +{ + const struct nbrec_logical_router_port *lrport; + + lrport = lrport_by_name_or_uuid(ctx, ctx->argv[1]); + if (!lrport) { + return; + } + + /* Find the router that contains 'lport', then delete it. */ + const struct nbrec_logical_router *lrouter; + NBREC_LOGICAL_ROUTER_FOR_EACH (lrouter, ctx->idl) { + for (size_t i = 0; i < lrouter->n_ports; i++) { + if (lrouter->ports[i] == lrport) { + remove_lrport(lrouter, i); + return; + } + } + } + + VLOG_WARN("logical router port %s is not part of any logical router", + ctx->argv[1]); +} + static const struct ctl_command_syntax nbctl_commands[] = { { "show", 0, 1, "[LSWITCH]", NULL, nbctl_show, NULL, "", RO }, @@ -1312,6 +1666,13 @@ static const struct ctl_command_syntax nbctl_commands[] = { NULL, "", RW }, { "lswitch-list", 0, 0, "", NULL, nbctl_lswitch_list, NULL, "", RO }, + /* lrouter commands. */ + { "lrouter-add", 0, 1, "[LROUTER]", NULL, nbctl_lrouter_add, + NULL, "", RW }, + { "lrouter-del", 1, 1, "LROUTER", NULL, nbctl_lrouter_del, + NULL, "", RW }, + { "lrouter-list", 0, 0, "", NULL, nbctl_lrouter_list, NULL, "", RO }, + /* acl commands. */ { "acl-add", 5, 5, "LSWITCH DIRECTION PRIORITY MATCH ACTION", NULL, nbctl_acl_add, NULL, "--log", RW }, @@ -1319,6 +1680,20 @@ static const struct ctl_command_syntax nbctl_commands[] = { nbctl_acl_del, NULL, "", RW }, { "acl-list", 1, 1, "LSWITCH", NULL, nbctl_acl_list, NULL, "", RO }, + /* lrport commands. */ + { "lrport-add", 2, 4, "LROUTER LRPORT ", NULL, nbctl_lrport_add, + NULL, "", RW }, + { "lrport-del", 1, 1, "LRPORT", NULL, nbctl_lrport_del, NULL, "", RO }, + { "lrport-list", 1, 1, "LROUTER", NULL, nbctl_lrport_list, NULL, "", RO }, + { "lrport-set-mac-address", 1, INT_MAX, "LRPORT [ADDRESS]", NULL, + nbctl_lrport_set_mac, NULL, "", RW }, + { "lrport-get-mac-address", 1, 1, "LRPORT", NULL, + nbctl_lrport_get_mac, NULL, + "", RO }, + { "lrport-set-enabled", 2, 2, "LRPORT STATE", NULL, + nbctl_lrport_set_enabled, NULL, "", RW }, + { "lrport-get-enabled", 1, 1, "LRPORT", NULL, + nbctl_lrport_get_enabled, NULL, "", RO }, /* lport commands. */ { "lport-add", 2, 4, "LSWITCH LPORT [PARENT] [TAG]", NULL, nbctl_lport_add, NULL, "", RW }, diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at index 85f8974..f4d009b 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -170,3 +170,72 @@ from-lport 200 (ip) drop OVN_NBCTL_TEST_STOP AT_CLEANUP + +dnl --------------------------------------------------------------------- + +AT_SETUP([ovn-nbctl - new commands ]) +OVN_NBCTL_TEST_START +dnl create a lswitch +AT_CHECK([ovn-nbctl lswitch-add ls0]) +dnl create a lrouter +AT_CHECK([ovn-nbctl lrouter-add lr0]) +dnl show command shows both lswitch and lrouter +AT_CHECK([ovn-nbctl show | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl + lswitch <0> (ls0) + lrouter <1> (lr0) +]) +dnl add another lrouter +AT_CHECK([ovn-nbctl lrouter-add lr1]) +dnl show <lrouter> to show a specific lrouter +AT_CHECK([ovn-nbctl show lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl + lrouter <0> (lr0) +]) +dnl lrouter-list shows all lrouters +AT_CHECK([ovn-nbctl lrouter-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +<0> (lr0) +<1> (lr1) +]) +dnl delete lrouter and check the list is correct +AT_CHECK([ovn-nbctl lrouter-del lr1]) +AT_CHECK([ovn-nbctl lrouter-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +<0> (lr0) +]) +dnl add two lrport +AT_CHECK([ovn-nbctl lrport-add lr0 lrport0]) +AT_CHECK([ovn-nbctl lrport-add lr0 lrport1]) +dnl check lrport-list is correct +AT_CHECK([ovn-nbctl lrport-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +<0> (lrport0) +<1> (lrport1) +]) +dnl delete lrport +AT_CHECK([ovn-nbctl lrport-del lrport1]) +dnl verify deletion +AT_CHECK([ovn-nbctl lrport-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +<0> (lrport0) +]) +dnl check lrport mac is uninitialzed +AT_CHECK([ovn-nbctl show lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl + lrouter <0> (lr0) + lrport lrport0 + mac: "" +]) +dnl set lrport mac +AT_CHECK([ovn-nbctl lrport-set-mac-address lrport0 "aa:bb:cc:dd:ee:ff"]) +dnl get lrport mac +AT_CHECK([ovn-nbctl lrport-get-mac-address lrport0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +aa:bb:cc:dd:ee:ff +]) +dnl get lrport enabled state (default:true) +AT_CHECK([ovn-nbctl lrport-get-enabled lrport0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +enabled +]) +dnl disable admin state of lrport +AT_CHECK([ovn-nbctl lrport-set-enabled lrport0 disabled]) +dnl verify lrport state +AT_CHECK([ovn-nbctl lrport-get-enabled lrport0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +disabled +]) + +OVN_NBCTL_TEST_STOP +AT_CLEANUP _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev