LCOV - code coverage report
Current view: top level - ovn/controller - encaps.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 103 111 92.8 %
Date: 2016-09-14 01:02:56 Functions: 7 7 100.0 %
Branches: 50 64 78.1 %

           Branch data     Line data    Source code
       1                 :            : /* Copyright (c) 2015, 2016 Nicira, Inc.
       2                 :            :  *
       3                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
       4                 :            :  * you may not use this file except in compliance with the License.
       5                 :            :  * You may obtain a copy of the License at:
       6                 :            :  *
       7                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
       8                 :            :  *
       9                 :            :  * Unless required by applicable law or agreed to in writing, software
      10                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      11                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12                 :            :  * See the License for the specific language governing permissions and
      13                 :            :  * limitations under the License.
      14                 :            :  */
      15                 :            : 
      16                 :            : #include <config.h>
      17                 :            : #include "encaps.h"
      18                 :            : 
      19                 :            : #include "lib/hash.h"
      20                 :            : #include "lib/sset.h"
      21                 :            : #include "lib/util.h"
      22                 :            : #include "lib/vswitch-idl.h"
      23                 :            : #include "openvswitch/vlog.h"
      24                 :            : #include "ovn/lib/ovn-sb-idl.h"
      25                 :            : #include "ovn-controller.h"
      26                 :            : 
      27                 :         94 : VLOG_DEFINE_THIS_MODULE(encaps);
      28                 :            : 
      29                 :            : void
      30                 :         44 : encaps_register_ovs_idl(struct ovsdb_idl *ovs_idl)
      31                 :            : {
      32                 :         44 :     ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
      33                 :         44 :     ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
      34                 :         44 :     ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
      35                 :         44 :     ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_name);
      36                 :         44 :     ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_interfaces);
      37                 :         44 :     ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_external_ids);
      38                 :         44 :     ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
      39                 :         44 :     ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name);
      40                 :         44 :     ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_type);
      41                 :         44 :     ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_options);
      42                 :         44 : }
      43                 :            : 
      44                 :            : /* Enough context to create a new tunnel, using tunnel_add(). */
      45                 :            : struct tunnel_ctx {
      46                 :            :     /* Maps from a chassis name to "struct chassis_node *". */
      47                 :            :     struct shash chassis;
      48                 :            : 
      49                 :            :     /* Names of all ports in the bridge, to allow checking uniqueness when
      50                 :            :      * adding a new tunnel. */
      51                 :            :     struct sset port_names;
      52                 :            : 
      53                 :            :     struct ovsdb_idl_txn *ovs_txn;
      54                 :            :     const struct ovsrec_bridge *br_int;
      55                 :            : };
      56                 :            : 
      57                 :            : struct chassis_node {
      58                 :            :     const struct ovsrec_port *port;
      59                 :            :     const struct ovsrec_bridge *bridge;
      60                 :            : };
      61                 :            : 
      62                 :            : static char *
      63                 :         34 : tunnel_create_name(struct tunnel_ctx *tc, const char *chassis_id)
      64                 :            : {
      65                 :            :     int i;
      66                 :            : 
      67         [ +  - ]:         34 :     for (i = 0; i < UINT16_MAX; i++) {
      68                 :            :         char *port_name;
      69                 :         34 :         port_name = xasprintf("ovn-%.6s-%x", chassis_id, i);
      70                 :            : 
      71         [ +  - ]:         34 :         if (!sset_contains(&tc->port_names, port_name)) {
      72                 :         34 :             return port_name;
      73                 :            :         }
      74                 :            : 
      75                 :          0 :         free(port_name);
      76                 :            :     }
      77                 :            : 
      78                 :          0 :     return NULL;
      79                 :            : }
      80                 :            : 
      81                 :            : static void
      82                 :       3330 : tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id,
      83                 :            :            const struct sbrec_encap *encap)
      84                 :            : {
      85                 :       3330 :     struct smap options = SMAP_INITIALIZER(&options);
      86                 :       3330 :     smap_add(&options, "remote_ip", encap->ip);
      87                 :       3330 :     smap_add(&options, "key", "flow");
      88                 :       3330 :     const char *csum = smap_get(&encap->options, "csum");
      89 [ +  + ][ +  + ]:       3330 :     if (csum && (!strcmp(csum, "true") || !strcmp(csum, "false"))) {
                 [ +  - ]
      90                 :       3304 :         smap_add(&options, "csum", csum);
      91                 :            :     }
      92                 :            : 
      93                 :            :     /* If there's an existing chassis record that does not need any change,
      94                 :            :      * keep it.  Otherwise, create a new record (if there was an existing
      95                 :            :      * record, the new record will supplant it and encaps_run() will delete
      96                 :            :      * it). */
      97                 :       3330 :     struct chassis_node *chassis = shash_find_data(&tc->chassis,
      98                 :            :                                                    new_chassis_id);
      99         [ +  + ]:       3330 :     if (chassis
     100         [ +  - ]:       3296 :         && chassis->port->n_interfaces == 1
     101         [ +  + ]:       3296 :         && !strcmp(chassis->port->interfaces[0]->type, encap->type)
     102         [ +  + ]:       3294 :         && smap_equal(&chassis->port->interfaces[0]->options, &options)) {
     103                 :       3293 :         shash_find_and_delete(&tc->chassis, new_chassis_id);
     104                 :       3293 :         free(chassis);
     105                 :       3293 :         goto exit;
     106                 :            :     }
     107                 :            : 
     108                 :            :     /* Choose a name for the new port.  If we're replacing an old port, reuse
     109                 :            :      * its name, otherwise generate a new, unique name. */
     110                 :         37 :     char *port_name = (chassis
     111                 :          3 :                        ? xstrdup(chassis->port->name)
     112         [ +  + ]:         37 :                        : tunnel_create_name(tc, new_chassis_id));
     113         [ -  + ]:         37 :     if (!port_name) {
     114         [ #  # ]:          0 :         VLOG_WARN("Unable to allocate unique name for '%s' tunnel",
     115                 :            :                   new_chassis_id);
     116                 :          0 :         goto exit;
     117                 :            :     }
     118                 :            : 
     119                 :         37 :     struct ovsrec_interface *iface = ovsrec_interface_insert(tc->ovs_txn);
     120                 :         37 :     ovsrec_interface_set_name(iface, port_name);
     121                 :         37 :     ovsrec_interface_set_type(iface, encap->type);
     122                 :         37 :     ovsrec_interface_set_options(iface, &options);
     123                 :            : 
     124                 :         37 :     struct ovsrec_port *port = ovsrec_port_insert(tc->ovs_txn);
     125                 :         37 :     ovsrec_port_set_name(port, port_name);
     126                 :         37 :     ovsrec_port_set_interfaces(port, &iface, 1);
     127                 :         37 :     const struct smap id = SMAP_CONST1(&id, "ovn-chassis-id", new_chassis_id);
     128                 :         37 :     ovsrec_port_set_external_ids(port, &id);
     129                 :            : 
     130                 :         37 :     ovsrec_bridge_update_ports_addvalue(tc->br_int, port);
     131                 :            : 
     132                 :         37 :     sset_add_and_free(&tc->port_names, port_name);
     133                 :            : 
     134                 :            : exit:
     135                 :       3330 :     smap_destroy(&options);
     136                 :       3330 : }
     137                 :            : 
     138                 :            : static struct sbrec_encap *
     139                 :       3330 : preferred_encap(const struct sbrec_chassis *chassis_rec)
     140                 :            : {
     141                 :       3330 :     struct sbrec_encap *best_encap = NULL;
     142                 :       3330 :     uint32_t best_type = 0;
     143                 :            : 
     144         [ +  + ]:       9962 :     for (int i = 0; i < chassis_rec->n_encaps; i++) {
     145                 :       6632 :         uint32_t tun_type = get_tunnel_type(chassis_rec->encaps[i]->type);
     146         [ +  + ]:       6632 :         if (tun_type > best_type) {
     147                 :       4707 :             best_type = tun_type;
     148                 :       4707 :             best_encap = chassis_rec->encaps[i];
     149                 :            :         }
     150                 :            :     }
     151                 :            : 
     152                 :       3330 :     return best_encap;
     153                 :            : }
     154                 :            : 
     155                 :            : void
     156                 :       3167 : encaps_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
     157                 :            :            const char *chassis_id)
     158                 :            : {
     159 [ +  + ][ -  + ]:       3167 :     if (!ctx->ovs_idl_txn || !br_int) {
     160                 :        174 :         return;
     161                 :            :     }
     162                 :            : 
     163                 :            :     const struct sbrec_chassis *chassis_rec;
     164                 :            :     const struct ovsrec_bridge *br;
     165                 :            : 
     166                 :       2993 :     struct tunnel_ctx tc = {
     167                 :            :         .chassis = SHASH_INITIALIZER(&tc.chassis),
     168                 :            :         .port_names = SSET_INITIALIZER(&tc.port_names),
     169                 :            :         .br_int = br_int
     170                 :            :     };
     171                 :            : 
     172                 :       2993 :     tc.ovs_txn = ctx->ovs_idl_txn;
     173                 :       2993 :     ovsdb_idl_txn_add_comment(tc.ovs_txn,
     174                 :            :                               "ovn-controller: modifying OVS tunnels '%s'",
     175                 :            :                               chassis_id);
     176                 :            : 
     177                 :            :     /* Collect all port names into tc.port_names.
     178                 :            :      *
     179                 :            :      * Collect all the OVN-created tunnels into tc.tunnel_hmap. */
     180         [ +  + ]:       9416 :     OVSREC_BRIDGE_FOR_EACH(br, ctx->ovs_idl) {
     181         [ +  + ]:      56034 :         for (size_t i = 0; i < br->n_ports; i++) {
     182                 :      49611 :             const struct ovsrec_port *port = br->ports[i];
     183                 :      49611 :             sset_add(&tc.port_names, port->name);
     184                 :            : 
     185                 :      49611 :             const char *id = smap_get(&port->external_ids, "ovn-chassis-id");
     186         [ +  + ]:      49611 :             if (id) {
     187         [ +  - ]:       3311 :                 if (!shash_find(&tc.chassis, id)) {
     188                 :       3311 :                     struct chassis_node *chassis = xzalloc(sizeof *chassis);
     189                 :       3311 :                     chassis->bridge = br;
     190                 :       3311 :                     chassis->port = port;
     191                 :       3311 :                     shash_add_assert(&tc.chassis, id, chassis);
     192                 :            :                 } else {
     193                 :            :                     /* Duplicate port for ovn-chassis-id.  Arbitrarily choose
     194                 :            :                      * to delete this one. */
     195                 :          0 :                     ovsrec_bridge_update_ports_delvalue(br, port);
     196                 :            :                 }
     197                 :            :             }
     198                 :            :         }
     199                 :            :     }
     200                 :            : 
     201         [ +  + ]:       9314 :     SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->ovnsb_idl) {
     202         [ +  + ]:       6321 :         if (strcmp(chassis_rec->name, chassis_id)) {
     203                 :            :             /* Create tunnels to the other chassis. */
     204                 :       3330 :             const struct sbrec_encap *encap = preferred_encap(chassis_rec);
     205         [ -  + ]:       3330 :             if (!encap) {
     206         [ #  # ]:          0 :                 VLOG_INFO("No supported encaps for '%s'", chassis_rec->name);
     207                 :          0 :                 continue;
     208                 :            :             }
     209                 :       3330 :             tunnel_add(&tc, chassis_rec->name, encap);
     210                 :            :         }
     211                 :            :     }
     212                 :            : 
     213                 :            :     /* Delete any existing OVN tunnels that were not still around. */
     214                 :            :     struct shash_node *node, *next_node;
     215 [ +  + ][ -  + ]:       3011 :     SHASH_FOR_EACH_SAFE (node, next_node, &tc.chassis) {
                 [ +  + ]
     216                 :         18 :         struct chassis_node *chassis = node->data;
     217                 :         18 :         ovsrec_bridge_update_ports_delvalue(chassis->bridge, chassis->port);
     218                 :         18 :         shash_delete(&tc.chassis, node);
     219                 :         18 :         free(chassis);
     220                 :            :     }
     221                 :       2993 :     shash_destroy(&tc.chassis);
     222                 :       2993 :     sset_destroy(&tc.port_names);
     223                 :            : }
     224                 :            : 
     225                 :            : /* Returns true if the database is all cleaned up, false if more work is
     226                 :            :  * required. */
     227                 :            : bool
     228                 :        138 : encaps_cleanup(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int)
     229                 :            : {
     230         [ -  + ]:        138 :     if (!br_int) {
     231                 :          0 :         return true;
     232                 :            :     }
     233                 :            : 
     234                 :            :     /* Delete all the OVS-created tunnels from the integration bridge. */
     235                 :        138 :     struct ovsrec_port **ports
     236                 :        138 :         = xmalloc(sizeof *br_int->ports * br_int->n_ports);
     237                 :        138 :     size_t n = 0;
     238         [ +  + ]:       1223 :     for (size_t i = 0; i < br_int->n_ports; i++) {
     239         [ +  + ]:       1085 :         if (!smap_get(&br_int->ports[i]->external_ids, "ovn-chassis-id")) {
     240                 :       1049 :             ports[n++] = br_int->ports[i];
     241                 :            :         }
     242                 :            :     }
     243                 :            : 
     244                 :        138 :     bool any_changes = n != br_int->n_ports;
     245 [ +  + ][ +  + ]:        138 :     if (any_changes && ctx->ovs_idl_txn) {
     246                 :         14 :         ovsdb_idl_txn_add_comment(ctx->ovs_idl_txn,
     247                 :            :                                   "ovn-controller: destroying tunnels");
     248                 :         14 :         ovsrec_bridge_verify_ports(br_int);
     249                 :         14 :         ovsrec_bridge_set_ports(br_int, ports, n);
     250                 :            :     }
     251                 :        138 :     free(ports);
     252                 :            : 
     253                 :        138 :     return !any_changes;
     254                 :            : }

Generated by: LCOV version 1.12