ovsdb-server: Refactoring and clean up remote status reporting.
[cascardo/ovs.git] / vtep / vtep-ctl.c
index 309c0b3..bf42f1c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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.
@@ -70,6 +70,13 @@ static int timeout;
 /* 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);
@@ -80,11 +87,13 @@ static void do_vtep_ctl(const char *args, struct ctl_command *, size_t n,
 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;
@@ -95,7 +104,7 @@ main(int argc, char *argv[])
     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();
@@ -128,6 +137,11 @@ main(int argc, char *argv[])
     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);
@@ -209,7 +223,7 @@ parse_options(int argc, char *argv[], struct shash *local_options)
             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:
@@ -270,6 +284,23 @@ parse_options(int argc, char *argv[], struct shash *local_options)
     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)
 {
@@ -305,6 +336,12 @@ Logical Switch commands:\n\
   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\
@@ -340,36 +377,48 @@ Other options:\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. */
@@ -396,9 +445,11 @@ struct vtep_ctl_context {
                              * 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)
 {
@@ -427,6 +478,11 @@ struct vtep_ctl_lswitch {
     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;
@@ -488,7 +544,7 @@ del_cached_port(struct vtep_ctl_context *vtepctl_ctx,
     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)
 {
@@ -497,7 +553,6 @@ add_pswitch_to_cache(struct vtep_ctl_context *vtepctl_ctx,
     ps->name = xstrdup(ps_cfg->name);
     list_init(&ps->ports);
     shash_add(&vtepctl_ctx->pswitches, ps->name, ps);
-    return ps;
 }
 
 static void
@@ -608,6 +663,28 @@ del_cached_ls_binding(struct vtep_ctl_port *port, const char *vlan)
     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)
@@ -752,6 +829,13 @@ vtep_ctl_context_invalidate_cache(struct ctl_context *ctx)
     }
     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
@@ -768,6 +852,8 @@ pre_get_info(struct ctl_context *ctx)
 
     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,
@@ -808,12 +894,14 @@ vtep_ctl_context_populate_cache(struct ctl_context *ctx)
     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) {
@@ -825,12 +913,12 @@ vtep_ctl_context_populate_cache(struct ctl_context *ctx)
     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)) {
@@ -838,10 +926,7 @@ vtep_ctl_context_populate_cache(struct ctl_context *ctx)
                       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];
@@ -866,6 +951,17 @@ vtep_ctl_context_populate_cache(struct ctl_context *ctx)
     }
     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;
 
@@ -1187,7 +1283,7 @@ cmd_ps_exists(struct ctl_context *ctx)
 
     vtep_ctl_context_populate_cache(ctx);
     if (!find_pswitch(vtepctl_ctx, ctx->argv[1], false)) {
-        ctl_exit(2);
+        vtep_ctl_exit(2);
     }
 }
 
@@ -1361,7 +1457,7 @@ cmd_ls_exists(struct ctl_context *ctx)
 
     vtep_ctl_context_populate_cache(ctx);
     if (!find_lswitch(vtepctl_ctx, ctx->argv[1], false)) {
-        ctl_exit(2);
+        vtep_ctl_exit(2);
     }
 }
 
@@ -1427,6 +1523,94 @@ cmd_unbind_ls(struct ctl_context *ctx)
     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)
 {
@@ -1955,7 +2139,7 @@ cmd_set_manager(struct ctl_context *ctx)
 }
 
 /* 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}}},
@@ -2008,6 +2192,18 @@ const struct ctl_table_class tables[] = {
      {{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}}}
 };
 
@@ -2264,6 +2460,12 @@ static const struct ctl_command_syntax vtep_commands[] = {
     {"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},
@@ -2303,6 +2505,6 @@ static const struct ctl_command_syntax vtep_commands[] = {
 static void
 vtep_ctl_cmd_init(void)
 {
-    ctl_init();
+    ctl_init(tables, cmd_show_tables, vtep_ctl_exit);
     ctl_register_commands(vtep_commands);
 }