/*
- * Copyright (c) 2009, 2010, 2011, 2012, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2014, 2015, 2016 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/* Format for table output. */
static struct table_style table_style = TABLE_STYLE_DEFAULT;
+/* The IDL we're using and the current transaction, if any.
+ * This is for use by vtep_ctl_exit() only, to allow it to clean up.
+ * Other code should use its context arguments. */
+static struct ovsdb_idl *the_idl;
+static struct ovsdb_idl_txn *the_idl_txn;
+
+OVS_NO_RETURN static void vtep_ctl_exit(int status);
static void vtep_ctl_cmd_init(void);
OVS_NO_RETURN static void usage(void);
static void parse_options(int argc, char *argv[], struct shash *local_options);
static struct vtep_ctl_lswitch *find_lswitch(struct vtep_ctl_context *,
const char *name,
bool must_exist);
+static struct vtep_ctl_lrouter *find_lrouter(struct vtep_ctl_context *,
+ const char *name,
+ bool must_exist);
int
main(int argc, char *argv[])
{
- extern struct vlog_module VLM_reconnect;
struct ovsdb_idl *idl;
struct ctl_command *commands;
struct shash local_options;
set_program_name(argv[0]);
fatal_ignore_sigpipe();
vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
- vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
+ vlog_set_levels_from_string_assert("reconnect:warn");
vteprec_init();
vtep_ctl_cmd_init();
seqno = ovsdb_idl_get_seqno(idl);
for (;;) {
ovsdb_idl_run(idl);
+ if (!ovsdb_idl_is_alive(idl)) {
+ int retval = ovsdb_idl_get_last_error(idl);
+ ctl_fatal("%s: database connection failed (%s)",
+ db, ovs_retval_to_string(retval));
+ }
if (seqno != ovsdb_idl_get_seqno(idl)) {
seqno = ovsdb_idl_get_seqno(idl);
break;
case OPT_NO_SYSLOG:
- vlog_set_levels(&VLM_vtep_ctl, VLF_SYSLOG, VLL_WARN);
+ vlog_set_levels(&this_module, VLF_SYSLOG, VLL_WARN);
break;
case OPT_DRY_RUN:
free(options);
}
+/* Frees the current transaction and the underlying IDL and then calls
+ * exit(status).
+ *
+ * Freeing the transaction and the IDL is not strictly necessary, but it makes
+ * for a clean memory leak report from valgrind in the normal case. That makes
+ * it easier to notice real memory leaks. */
+static void
+vtep_ctl_exit(int status)
+{
+ if (the_idl_txn) {
+ ovsdb_idl_txn_abort(the_idl_txn);
+ ovsdb_idl_txn_destroy(the_idl_txn);
+ }
+ ovsdb_idl_destroy(the_idl);
+ exit(status);
+}
+
static void
usage(void)
{
unbind-ls PS PORT VLAN unbind logical switch on VLAN from PORT\n\
list-bindings PS PORT list bindings for PORT on PS\n\
\n\
+Logical Router commands:\n\
+ add-lr LR create a new logical router named LR\n\
+ del-lr LR delete LR\n\
+ list-lr print the names of all the logical routers\n\
+ lr-exists LR exit 2 if LR does not exist\n\
+\n\
MAC binding commands:\n\
add-ucast-local LS MAC [ENCAP] IP add ucast local entry in LS\n\
del-ucast-local LS MAC del ucast local entry from LS\n\
}
\f
-struct cmd_show_table cmd_show_tables[] = {
+static struct cmd_show_table cmd_show_tables[] = {
{&vteprec_table_global,
NULL,
{&vteprec_global_col_managers,
&vteprec_global_col_switches,
NULL},
- false},
+ {NULL, NULL, NULL}
+ },
{&vteprec_table_manager,
&vteprec_manager_col_target,
{&vteprec_manager_col_is_connected,
NULL,
NULL},
- false},
+ {NULL, NULL, NULL}
+ },
{&vteprec_table_physical_switch,
&vteprec_physical_switch_col_name,
- {&vteprec_physical_switch_col_tunnel_ips,
- &vteprec_physical_switch_col_ports,
- NULL},
- false},
+ {&vteprec_physical_switch_col_management_ips,
+ &vteprec_physical_switch_col_tunnel_ips,
+ &vteprec_physical_switch_col_ports},
+ {NULL, NULL, NULL}
+ },
{&vteprec_table_physical_port,
&vteprec_physical_port_col_name,
{&vteprec_physical_port_col_vlan_bindings,
NULL,
NULL},
- false},
+ {NULL, NULL, NULL}
+ },
+
+ {&vteprec_table_logical_switch,
+ &vteprec_logical_switch_col_name,
+ {NULL,
+ NULL,
+ NULL},
+ {NULL, NULL, NULL}
+ },
- {NULL, NULL, {NULL, NULL, NULL}, false}
+ {NULL, NULL, {NULL, NULL, NULL}, {NULL, NULL, NULL}}
};
/* vtep-ctl specific context. Inherits the 'struct ctl_context' as base. */
* struct vtep_ctl_lswitch. */
struct shash plocs; /* Maps from "<encap>+<dst_ip>" to
* struct vteprec_physical_locator. */
+ struct shash lrouters; /* Maps from logical router name to
+ * struct vtep_ctl_lrouter. */
};
-/* Casts 'base' into 'strcut vtep_ctl_context'. */
+/* Casts 'base' into 'struct vtep_ctl_context'. */
static struct vtep_ctl_context *
vtep_ctl_context_cast(struct ctl_context *base)
{
struct shash mcast_remote; /* Maps from mac to vtep_ctl_mcast_mac. */
};
+struct vtep_ctl_lrouter {
+ const struct vteprec_logical_router *lr_cfg;
+ char *name;
+};
+
struct vtep_ctl_mcast_mac {
const struct vteprec_mcast_macs_local *local_cfg;
const struct vteprec_mcast_macs_remote *remote_cfg;
free(port);
}
-static struct vtep_ctl_pswitch *
+static void
add_pswitch_to_cache(struct vtep_ctl_context *vtepctl_ctx,
struct vteprec_physical_switch *ps_cfg)
{
ps->name = xstrdup(ps_cfg->name);
list_init(&ps->ports);
shash_add(&vtepctl_ctx->pswitches, ps->name, ps);
- return ps;
}
static void
shash_find_and_delete(&port->bindings, vlan);
}
+static struct vtep_ctl_lrouter *
+add_lrouter_to_cache(struct vtep_ctl_context *vtepctl_ctx,
+ const struct vteprec_logical_router *lr_cfg)
+{
+ struct vtep_ctl_lrouter *lr = xmalloc(sizeof *lr);
+ lr->lr_cfg = lr_cfg;
+ lr->name = xstrdup(lr_cfg->name);
+ shash_add(&vtepctl_ctx->lrouters, lr->name, lr);
+ return lr;
+}
+
+static void
+del_cached_lrouter(struct vtep_ctl_context *ctx, struct vtep_ctl_lrouter *lr)
+{
+ if (lr->lr_cfg) {
+ vteprec_logical_router_delete(lr->lr_cfg);
+ }
+ shash_find_and_delete(&ctx->lrouters, lr->name);
+ free(lr->name);
+ free(lr);
+}
+
static struct vteprec_physical_locator *
find_ploc(struct vtep_ctl_context *vtepctl_ctx, const char *encap,
const char *dst_ip)
}
shash_destroy(&vtepctl_ctx->lswitches);
shash_destroy(&vtepctl_ctx->plocs);
+
+ SHASH_FOR_EACH (node, &vtepctl_ctx->lrouters) {
+ struct vtep_ctl_lrouter *lr = node->data;
+ free(lr->name);
+ free(lr);
+ }
+ shash_destroy(&vtepctl_ctx->lrouters);
}
static void
ovsdb_idl_add_column(ctx->idl, &vteprec_logical_switch_col_name);
+ ovsdb_idl_add_column(ctx->idl, &vteprec_logical_router_col_name);
+
ovsdb_idl_add_column(ctx->idl, &vteprec_ucast_macs_local_col_MAC);
ovsdb_idl_add_column(ctx->idl, &vteprec_ucast_macs_local_col_locator);
ovsdb_idl_add_column(ctx->idl,
struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
const struct vteprec_global *vtep_global = vtepctl_ctx->vtep_global;
const struct vteprec_logical_switch *ls_cfg;
+ const struct vteprec_logical_router *lr_cfg;
const struct vteprec_ucast_macs_local *ucast_local_cfg;
const struct vteprec_ucast_macs_remote *ucast_remote_cfg;
const struct vteprec_mcast_macs_local *mcast_local_cfg;
const struct vteprec_mcast_macs_remote *mcast_remote_cfg;
const struct vteprec_tunnel *tunnel_cfg;
struct sset pswitches, ports, lswitches;
+ struct sset lrouters;
size_t i;
if (vtepctl_ctx->cache_valid) {
shash_init(&vtepctl_ctx->ports);
shash_init(&vtepctl_ctx->lswitches);
shash_init(&vtepctl_ctx->plocs);
+ shash_init(&vtepctl_ctx->lrouters);
sset_init(&pswitches);
sset_init(&ports);
for (i = 0; i < vtep_global->n_switches; i++) {
struct vteprec_physical_switch *ps_cfg = vtep_global->switches[i];
- struct vtep_ctl_pswitch *ps;
size_t j;
if (!sset_add(&pswitches, ps_cfg->name)) {
ps_cfg->name);
continue;
}
- ps = add_pswitch_to_cache(vtepctl_ctx, ps_cfg);
- if (!ps) {
- continue;
- }
+ add_pswitch_to_cache(vtepctl_ctx, ps_cfg);
for (j = 0; j < ps_cfg->n_ports; j++) {
struct vteprec_physical_port *port_cfg = ps_cfg->ports[j];
}
sset_destroy(&lswitches);
+ sset_init(&lrouters);
+ VTEPREC_LOGICAL_ROUTER_FOR_EACH (lr_cfg, ctx->idl) {
+ if (!sset_add(&lrouters, lr_cfg->name)) {
+ VLOG_WARN("%s: database contains duplicate logical router name",
+ lr_cfg->name);
+ continue;
+ }
+ add_lrouter_to_cache(vtepctl_ctx, lr_cfg);
+ }
+ sset_destroy(&lrouters);
+
VTEPREC_UCAST_MACS_LOCAL_FOR_EACH (ucast_local_cfg, ctx->idl) {
struct vtep_ctl_lswitch *ls;
vtep_ctl_context_populate_cache(ctx);
if (!find_pswitch(vtepctl_ctx, ctx->argv[1], false)) {
- ctl_exit(2);
+ vtep_ctl_exit(2);
}
}
vtep_ctl_context_populate_cache(ctx);
if (!find_lswitch(vtepctl_ctx, ctx->argv[1], false)) {
- ctl_exit(2);
+ vtep_ctl_exit(2);
}
}
vtep_ctl_context_invalidate_cache(ctx);
}
+static struct vtep_ctl_lrouter *
+find_lrouter(struct vtep_ctl_context *vtepctl_ctx,
+ const char *name, bool must_exist)
+{
+ struct vtep_ctl_lrouter *lr;
+
+ ovs_assert(vtepctl_ctx->cache_valid);
+
+ lr = shash_find_data(&vtepctl_ctx->lrouters, name);
+ if (must_exist && !lr) {
+ ctl_fatal("no logical router named %s", name);
+ }
+ return lr;
+}
+
+static void
+cmd_add_lr(struct ctl_context *ctx)
+{
+ struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
+ const char *lr_name = ctx->argv[1];
+ bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
+ struct vteprec_logical_router *lr;
+
+ vtep_ctl_context_populate_cache(ctx);
+ if (find_lrouter(vtepctl_ctx, lr_name, false)) {
+ if (!may_exist) {
+ ctl_fatal("cannot create logical router %s because it "
+ "already exists", lr_name);
+ }
+ return;
+ }
+
+ lr = vteprec_logical_router_insert(ctx->txn);
+ vteprec_logical_router_set_name(lr, lr_name);
+
+ vtep_ctl_context_invalidate_cache(ctx);
+}
+
+static void
+del_lrouter(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_lrouter *lr)
+{
+ del_cached_lrouter(vtepctl_ctx, lr);
+}
+
+static void
+cmd_del_lr(struct ctl_context *ctx)
+{
+ struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
+ bool must_exist = !shash_find(&ctx->options, "--if-exists");
+ struct vtep_ctl_lrouter *lr;
+
+ vtep_ctl_context_populate_cache(ctx);
+ lr = find_lrouter(vtepctl_ctx, ctx->argv[1], must_exist);
+ if (lr) {
+ del_lrouter(vtepctl_ctx, lr);
+ }
+}
+
+static void
+cmd_list_lr(struct ctl_context *ctx)
+{
+ struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
+ struct shash_node *node;
+ struct svec lrouters;
+
+ vtep_ctl_context_populate_cache(ctx);
+
+ svec_init(&lrouters);
+ SHASH_FOR_EACH (node, &vtepctl_ctx->lrouters) {
+ struct vtep_ctl_lrouter *lr = node->data;
+
+ svec_add(&lrouters, lr->name);
+ }
+ output_sorted(&lrouters, &ctx->output);
+ svec_destroy(&lrouters);
+}
+
+static void
+cmd_lr_exists(struct ctl_context *ctx)
+{
+ struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
+
+ vtep_ctl_context_populate_cache(ctx);
+ if (!find_lrouter(vtepctl_ctx, ctx->argv[1], false)) {
+ vtep_ctl_exit(2);
+ }
+}
+
static void
add_ucast_entry(struct ctl_context *ctx, bool local)
{
}
/* Parameter commands. */
-const struct ctl_table_class tables[] = {
+static const struct ctl_table_class tables[] = {
{&vteprec_table_global,
{{&vteprec_table_global, NULL, NULL},
{NULL, NULL, NULL}}},
{{NULL, NULL, NULL},
{NULL, NULL, NULL}}},
+ {&vteprec_table_logical_router,
+ {{&vteprec_table_logical_router, &vteprec_logical_router_col_name, NULL},
+ {NULL, NULL, NULL}}},
+
+ {&vteprec_table_arp_sources_local,
+ {{NULL, NULL, NULL},
+ {NULL, NULL, NULL}}},
+
+ {&vteprec_table_arp_sources_remote,
+ {{NULL, NULL, NULL},
+ {NULL, NULL, NULL}}},
+
{NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
};
{"bind-ls", 4, 4, NULL, pre_get_info, cmd_bind_ls, NULL, "", RO},
{"unbind-ls", 3, 3, NULL, pre_get_info, cmd_unbind_ls, NULL, "", RO},
+ /* Logical Router commands. */
+ {"add-lr", 1, 1, NULL, pre_get_info, cmd_add_lr, NULL, "--may-exist", RW},
+ {"del-lr", 1, 1, NULL, pre_get_info, cmd_del_lr, NULL, "--if-exists", RW},
+ {"list-lr", 0, 0, NULL, pre_get_info, cmd_list_lr, NULL, "", RO},
+ {"lr-exists", 1, 1, NULL, pre_get_info, cmd_lr_exists, NULL, "", RO},
+
/* MAC binding commands. */
{"add-ucast-local", 3, 4, NULL, pre_get_info, cmd_add_ucast_local, NULL,
"", RW},
static void
vtep_ctl_cmd_init(void)
{
- ctl_init();
+ ctl_init(tables, cmd_show_tables, vtep_ctl_exit);
ctl_register_commands(vtep_commands);
}