LCOV - code coverage report
Current view: top level - ovn/controller - patch.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 153 168 91.1 %
Date: 2016-09-14 01:02:56 Functions: 11 11 100.0 %
Branches: 88 122 72.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                 :            : 
      18                 :            : #include "patch.h"
      19                 :            : 
      20                 :            : #include "hash.h"
      21                 :            : #include "lflow.h"
      22                 :            : #include "lib/vswitch-idl.h"
      23                 :            : #include "lport.h"
      24                 :            : #include "openvswitch/hmap.h"
      25                 :            : #include "openvswitch/vlog.h"
      26                 :            : #include "ovn-controller.h"
      27                 :            : 
      28                 :         94 : VLOG_DEFINE_THIS_MODULE(patch);
      29                 :            : 
      30                 :            : static char *
      31                 :      48664 : patch_port_name(const char *src, const char *dst)
      32                 :            : {
      33                 :      48664 :     return xasprintf("patch-%s-to-%s", src, dst);
      34                 :            : }
      35                 :            : 
      36                 :            : /* Return true if 'port' is a patch port with the specified 'peer'. */
      37                 :            : static bool
      38                 :     311307 : match_patch_port(const struct ovsrec_port *port, const char *peer)
      39                 :            : {
      40         [ +  + ]:     598156 :     for (size_t i = 0; i < port->n_interfaces; i++) {
      41                 :     311307 :         struct ovsrec_interface *iface = port->interfaces[i];
      42         [ +  + ]:     311307 :         if (strcmp(iface->type, "patch")) {
      43                 :     101455 :             continue;
      44                 :            :         }
      45                 :     209852 :         const char *iface_peer = smap_get(&iface->options, "peer");
      46 [ +  - ][ +  + ]:     209852 :         if (peer && !strcmp(iface_peer, peer)) {
      47                 :      24458 :             return true;
      48                 :            :         }
      49                 :            :     }
      50                 :     286849 :     return false;
      51                 :            : }
      52                 :            : 
      53                 :            : /* Creates a patch port in bridge 'src' named 'src_name', whose peer is
      54                 :            :  * 'dst_name' in bridge 'dst'.  Initializes the patch port's external-ids:'key'
      55                 :            :  * to 'key'.
      56                 :            :  *
      57                 :            :  * If such a patch port already exists, removes it from 'existing_ports'. */
      58                 :            : static void
      59                 :      24685 : create_patch_port(struct controller_ctx *ctx,
      60                 :            :                   const char *key, const char *value,
      61                 :            :                   const struct ovsrec_bridge *src, const char *src_name,
      62                 :            :                   const struct ovsrec_bridge *dst, const char *dst_name,
      63                 :            :                   struct shash *existing_ports)
      64                 :            : {
      65         [ +  + ]:     311534 :     for (size_t i = 0; i < src->n_ports; i++) {
      66         [ +  + ]:     311307 :         if (match_patch_port(src->ports[i], dst_name)) {
      67                 :            :             /* Patch port already exists on 'src'. */
      68                 :      24458 :             shash_find_and_delete(existing_ports, src->ports[i]->name);
      69                 :      24458 :             return;
      70                 :            :         }
      71                 :            :     }
      72                 :            : 
      73                 :        227 :     ovsdb_idl_txn_add_comment(ctx->ovs_idl_txn,
      74                 :            :             "ovn-controller: creating patch port '%s' from '%s' to '%s'",
      75                 :            :             src_name, src->name, dst->name);
      76                 :            : 
      77                 :            :     struct ovsrec_interface *iface;
      78                 :        227 :     iface = ovsrec_interface_insert(ctx->ovs_idl_txn);
      79                 :        227 :     ovsrec_interface_set_name(iface, src_name);
      80                 :        227 :     ovsrec_interface_set_type(iface, "patch");
      81                 :        227 :     const struct smap options = SMAP_CONST1(&options, "peer", dst_name);
      82                 :        227 :     ovsrec_interface_set_options(iface, &options);
      83                 :            : 
      84                 :            :     struct ovsrec_port *port;
      85                 :        227 :     port = ovsrec_port_insert(ctx->ovs_idl_txn);
      86                 :        227 :     ovsrec_port_set_name(port, src_name);
      87                 :        227 :     ovsrec_port_set_interfaces(port, &iface, 1);
      88                 :        227 :     const struct smap ids = SMAP_CONST1(&ids, key, value);
      89                 :        227 :     ovsrec_port_set_external_ids(port, &ids);
      90                 :            : 
      91                 :            :     struct ovsrec_port **ports;
      92                 :        227 :     ports = xmalloc(sizeof *ports * (src->n_ports + 1));
      93                 :        227 :     memcpy(ports, src->ports, sizeof *ports * src->n_ports);
      94                 :        227 :     ports[src->n_ports] = port;
      95                 :        227 :     ovsrec_bridge_verify_ports(src);
      96                 :        227 :     ovsrec_bridge_set_ports(src, ports, src->n_ports + 1);
      97                 :            : 
      98                 :        227 :     free(ports);
      99                 :            : }
     100                 :            : 
     101                 :            : static void
     102                 :         40 : remove_port(struct controller_ctx *ctx,
     103                 :            :             const struct ovsrec_port *port)
     104                 :            : {
     105                 :            :     const struct ovsrec_bridge *bridge;
     106                 :            : 
     107                 :            :     /* We know the port we want to delete, but we have to find the bridge its
     108                 :            :      * on to do so.  Note this only runs on a config change that should be
     109                 :            :      * pretty rare. */
     110         [ +  - ]:         85 :     OVSREC_BRIDGE_FOR_EACH (bridge, ctx->ovs_idl) {
     111                 :            :         size_t i;
     112         [ +  + ]:        287 :         for (i = 0; i < bridge->n_ports; i++) {
     113         [ +  + ]:        242 :             if (bridge->ports[i] != port) {
     114                 :        202 :                 continue;
     115                 :            :             }
     116                 :            :             struct ovsrec_port **new_ports;
     117                 :         40 :             new_ports = xmemdup(bridge->ports,
     118                 :         40 :                     sizeof *new_ports * (bridge->n_ports - 1));
     119         [ +  + ]:         40 :             if (i != bridge->n_ports - 1) {
     120                 :            :                 /* Removed port was not last */
     121                 :         31 :                 new_ports[i] = bridge->ports[bridge->n_ports - 1];
     122                 :            :             }
     123                 :         40 :             ovsrec_bridge_verify_ports(bridge);
     124                 :         40 :             ovsrec_bridge_set_ports(bridge, new_ports, bridge->n_ports - 1);
     125                 :         40 :             free(new_ports);
     126                 :         40 :             ovsrec_port_delete(port);
     127                 :         40 :             return;
     128                 :            :         }
     129                 :            :     }
     130                 :            : }
     131                 :            : 
     132                 :            : /* Obtains external-ids:ovn-bridge-mappings from OVSDB and adds patch ports for
     133                 :            :  * the local bridge mappings.  Removes any patch ports for bridge mappings that
     134                 :            :  * already existed from 'existing_ports'. */
     135                 :            : static void
     136                 :       2993 : add_bridge_mappings(struct controller_ctx *ctx,
     137                 :            :                     const struct ovsrec_bridge *br_int,
     138                 :            :                     struct shash *existing_ports,
     139                 :            :                     struct hmap *local_datapaths,
     140                 :            :                     const char *chassis_id)
     141                 :            : {
     142                 :            :     /* Get ovn-bridge-mappings. */
     143                 :       2993 :     const char *mappings_cfg = "";
     144                 :            :     const struct ovsrec_open_vswitch *cfg;
     145                 :       2993 :     cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
     146         [ +  - ]:       2993 :     if (cfg) {
     147                 :       2993 :         mappings_cfg = smap_get(&cfg->external_ids, "ovn-bridge-mappings");
     148 [ +  + ][ -  + ]:       2993 :         if (!mappings_cfg || !mappings_cfg[0]) {
     149                 :       2729 :             return;
     150                 :            :         }
     151                 :            :     }
     152                 :            : 
     153                 :            :     /* Parse bridge mappings. */
     154                 :        264 :     struct shash bridge_mappings = SHASH_INITIALIZER(&bridge_mappings);
     155                 :            :     char *cur, *next, *start;
     156                 :        264 :     next = start = xstrdup(mappings_cfg);
     157 [ +  + ][ +  - ]:        531 :     while ((cur = strsep(&next, ",")) && *cur) {
     158                 :        282 :         char *network, *bridge = cur;
     159                 :            :         const struct ovsrec_bridge *ovs_bridge;
     160                 :            : 
     161                 :        282 :         network = strsep(&bridge, ":");
     162 [ +  + ][ +  - ]:        282 :         if (!bridge || !*network || !*bridge) {
                 [ -  + ]
     163         [ +  - ]:         15 :             VLOG_ERR("Invalid ovn-bridge-mappings configuration: '%s'",
     164                 :            :                     mappings_cfg);
     165                 :         15 :             break;
     166                 :            :         }
     167                 :            : 
     168                 :        267 :         ovs_bridge = get_bridge(ctx->ovs_idl, bridge);
     169         [ -  + ]:        267 :         if (!ovs_bridge) {
     170         [ #  # ]:          0 :             VLOG_WARN("Bridge '%s' not found for network '%s'",
     171                 :            :                     bridge, network);
     172                 :          0 :             continue;
     173                 :            :         }
     174                 :            : 
     175                 :        267 :         shash_add(&bridge_mappings, network, ovs_bridge);
     176                 :            :     }
     177                 :        264 :     free(start);
     178                 :            : 
     179                 :            :     const struct sbrec_port_binding *binding;
     180         [ +  + ]:       1966 :     SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) {
     181                 :            :         const char *patch_port_id;
     182         [ +  + ]:       1702 :         if (!strcmp(binding->type, "localnet")) {
     183                 :        587 :             struct local_datapath *ld
     184                 :        587 :                 = get_local_datapath(local_datapaths,
     185                 :        587 :                                      binding->datapath->tunnel_key);
     186         [ +  + ]:        587 :             if (!ld) {
     187                 :            :                 /* This localnet port is on a datapath with no
     188                 :            :                  * logical ports bound to this chassis, so there's no need
     189                 :            :                  * to create patch ports for it. */
     190                 :        232 :                 continue;
     191                 :            :             }
     192                 :            : 
     193                 :            :             /* Under incremental processing, it is possible to re-enter the
     194                 :            :              * following block with a logical port that has already been
     195                 :            :              * recorded in binding->logical_port.  Rather than emit spurious
     196                 :            :              * warnings, add a check to see if the logical port name has
     197                 :            :              * actually changed. */
     198                 :            : 
     199 [ -  + ][ #  # ]:        355 :             if (ld->localnet_port && strcmp(ld->localnet_port->logical_port,
     200                 :          0 :                                             binding->logical_port)) {
     201                 :            :                 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
     202         [ #  # ]:          0 :                 VLOG_WARN_RL(&rl, "localnet port '%s' already set for datapath "
     203                 :            :                              "'%"PRId64"', skipping the new port '%s'.",
     204                 :            :                              ld->localnet_port->logical_port,
     205                 :            :                              binding->datapath->tunnel_key,
     206                 :            :                              binding->logical_port);
     207                 :          0 :                 continue;
     208                 :            :             }
     209                 :        355 :             ld->localnet_port = binding;
     210                 :        355 :             patch_port_id = "ovn-localnet-port";
     211         [ -  + ]:       1115 :         } else if (!strcmp(binding->type, "l2gateway")) {
     212         [ #  # ]:          0 :             if (!binding->chassis
     213         [ #  # ]:          0 :                 || strcmp(chassis_id, binding->chassis->name)) {
     214                 :            :                 /* This L2 gateway port is not bound to this chassis,
     215                 :            :                  * so we should not create any patch ports for it. */
     216                 :          0 :                 continue;
     217                 :            :             }
     218                 :          0 :             patch_port_id = "ovn-l2gateway-port";
     219                 :            :         } else {
     220                 :            :             /* not a localnet or L2 gateway port. */
     221                 :       1115 :             continue;
     222                 :            :         }
     223                 :            : 
     224                 :        355 :         const char *network = smap_get(&binding->options, "network_name");
     225         [ +  + ]:        355 :         if (!network) {
     226                 :            :             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
     227         [ +  - ]:          2 :             VLOG_ERR_RL(&rl, "%s port '%s' has no network name.",
     228                 :            :                          binding->type, binding->logical_port);
     229                 :          2 :             continue;
     230                 :            :         }
     231                 :        353 :         struct ovsrec_bridge *br_ln = shash_find_data(&bridge_mappings, network);
     232         [ -  + ]:        353 :         if (!br_ln) {
     233                 :            :             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
     234         [ #  # ]:          0 :             VLOG_ERR_RL(&rl, "bridge not found for %s port '%s' "
     235                 :            :                     "with network name '%s'",
     236                 :            :                     binding->type, binding->logical_port, network);
     237                 :          0 :             continue;
     238                 :            :         }
     239                 :            : 
     240                 :        353 :         char *name1 = patch_port_name(br_int->name, binding->logical_port);
     241                 :        353 :         char *name2 = patch_port_name(binding->logical_port, br_int->name);
     242                 :        353 :         create_patch_port(ctx, patch_port_id, binding->logical_port,
     243                 :            :                           br_int, name1, br_ln, name2, existing_ports);
     244                 :        353 :         create_patch_port(ctx, patch_port_id, binding->logical_port,
     245                 :            :                           br_ln, name2, br_int, name1, existing_ports);
     246                 :        353 :         free(name1);
     247                 :        353 :         free(name2);
     248                 :            :     }
     249                 :            : 
     250                 :        264 :     shash_destroy(&bridge_mappings);
     251                 :            : }
     252                 :            : 
     253                 :            : static void
     254                 :      23979 : add_patched_datapath(struct hmap *patched_datapaths,
     255                 :            :                      const struct sbrec_port_binding *binding_rec, bool local)
     256                 :            : {
     257                 :      23979 :     struct patched_datapath *pd = get_patched_datapath(patched_datapaths,
     258                 :      23979 :                                        binding_rec->datapath->tunnel_key);
     259         [ +  + ]:      23979 :     if (pd) {
     260                 :            :         /* If the patched datapath is referenced by a logical patch port it is
     261                 :            :          * not stale, by definition, so set 'stale' to false */
     262                 :      16993 :         pd->stale = false;
     263                 :      16993 :         return;
     264                 :            :     }
     265                 :            : 
     266                 :       6986 :     pd = xzalloc(sizeof *pd);
     267                 :       6986 :     pd->local = local;
     268                 :       6986 :     pd->key = binding_rec->datapath->header_.uuid;
     269                 :            :     /* stale is set to false. */
     270                 :       6986 :     hmap_insert(patched_datapaths, &pd->hmap_node,
     271                 :            :                 binding_rec->datapath->tunnel_key);
     272                 :            : }
     273                 :            : 
     274                 :            : static void
     275                 :       2991 : add_logical_patch_ports_preprocess(struct hmap *patched_datapaths)
     276                 :            : {
     277                 :            :     /* Mark all patched datapaths as stale for later cleanup by
     278                 :            :      * add_logical_patch_ports_postprocess(). */
     279                 :            :     struct patched_datapath *pd;
     280 [ -  + ][ -  + ]:       2991 :     HMAP_FOR_EACH (pd, hmap_node, patched_datapaths) {
     281                 :          0 :         pd->stale = true;
     282                 :            :     }
     283                 :       2991 : }
     284                 :            : 
     285                 :            : /* This function should cleanup stale patched datapaths and any memory
     286                 :            :  * allocated for fields within a stale patched datapath. */
     287                 :            : static void
     288                 :       2991 : add_logical_patch_ports_postprocess(struct hmap *patched_datapaths)
     289                 :            : {
     290                 :            :     /* Clean up stale patched datapaths. */
     291                 :            :     struct patched_datapath *pd_cur_node, *pd_next_node;
     292 [ +  + ][ -  + ]:       9977 :     HMAP_FOR_EACH_SAFE (pd_cur_node, pd_next_node, hmap_node,
                 [ +  + ]
     293                 :            :                         patched_datapaths) {
     294         [ -  + ]:       6986 :         if (pd_cur_node->stale == true) {
     295                 :          0 :             hmap_remove(patched_datapaths, &pd_cur_node->hmap_node);
     296                 :          0 :             free(pd_cur_node);
     297                 :            :         }
     298                 :            :     }
     299                 :       2991 : }
     300                 :            : 
     301                 :            : /* Add one OVS patch port for each OVN logical patch port.
     302                 :            :  *
     303                 :            :  * This is suboptimal for several reasons.  First, it creates an OVS port for
     304                 :            :  * every OVN logical patch port, not just for the ones that are actually useful
     305                 :            :  * on this hypervisor.  Second, it's wasteful to create an OVS patch port per
     306                 :            :  * OVN logical patch port, when really there's no benefit to them beyond a way
     307                 :            :  * to identify how a packet ingressed into a logical datapath.
     308                 :            :  *
     309                 :            :  * There are two obvious ways to improve the situation here, by modifying
     310                 :            :  * OVS:
     311                 :            :  *
     312                 :            :  *     1. Add a way to configure in OVS which fields are preserved on a hop
     313                 :            :  *        across an OVS patch port.  If MFF_LOG_DATAPATH and MFF_LOG_INPORT
     314                 :            :  *        were preserved, then only a single pair of OVS patch ports would be
     315                 :            :  *        required regardless of the number of OVN logical patch ports.
     316                 :            :  *
     317                 :            :  *     2. Add a new OpenFlow extension action modeled on "resubmit" that also
     318                 :            :  *        saves and restores the packet data and metadata (the inability to do
     319                 :            :  *        this is the only reason that "resubmit" can't be used already).  Or
     320                 :            :  *        add OpenFlow extension actions to otherwise save and restore packet
     321                 :            :  *        data and metadata.
     322                 :            :  */
     323                 :            : static void
     324                 :       2993 : add_logical_patch_ports(struct controller_ctx *ctx,
     325                 :            :                         const struct ovsrec_bridge *br_int,
     326                 :            :                         const char *local_chassis_id,
     327                 :            :                         struct shash *existing_ports,
     328                 :            :                         struct hmap *patched_datapaths)
     329                 :            : {
     330                 :            :     const struct sbrec_chassis *chassis_rec;
     331                 :       2993 :     chassis_rec = get_chassis(ctx->ovnsb_idl, local_chassis_id);
     332         [ +  + ]:       2993 :     if (!chassis_rec) {
     333                 :          2 :         return;
     334                 :            :     }
     335                 :            : 
     336                 :       2991 :     add_logical_patch_ports_preprocess(patched_datapaths);
     337                 :            : 
     338                 :            :     const struct sbrec_port_binding *binding;
     339         [ +  + ]:      64224 :     SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) {
     340                 :      61233 :         const char *patch_port_id = "ovn-logical-patch-port";
     341                 :      61233 :         bool local_port = false;
     342         [ +  + ]:      61233 :         if (!strcmp(binding->type, "l3gateway")) {
     343                 :        819 :             const char *chassis = smap_get(&binding->options,
     344                 :            :                                            "l3gateway-chassis");
     345 [ +  - ][ +  + ]:        819 :             if (chassis && !strcmp(local_chassis_id, chassis)) {
     346                 :        707 :                 local_port = true;
     347                 :        707 :                 patch_port_id = "ovn-l3gateway-port";
     348                 :            :             }
     349                 :            :         }
     350                 :            : 
     351 [ +  + ][ +  + ]:      61233 :         if (!strcmp(binding->type, "patch") || local_port) {
     352                 :      23979 :             const char *local = binding->logical_port;
     353                 :      23979 :             const char *peer = smap_get(&binding->options, "peer");
     354         [ -  + ]:      23979 :             if (!peer) {
     355                 :          0 :                 continue;
     356                 :            :             }
     357                 :            : 
     358                 :      23979 :             char *src_name = patch_port_name(local, peer);
     359                 :      23979 :             char *dst_name = patch_port_name(peer, local);
     360                 :      23979 :             create_patch_port(ctx, patch_port_id, local,
     361                 :            :                               br_int, src_name, br_int, dst_name,
     362                 :            :                               existing_ports);
     363                 :      23979 :             free(dst_name);
     364                 :      23979 :             free(src_name);
     365                 :      23979 :             add_patched_datapath(patched_datapaths, binding, local_port);
     366         [ +  + ]:      23979 :             if (local_port) {
     367 [ +  + ][ +  - ]:        707 :                 if (binding->chassis != chassis_rec && ctx->ovnsb_idl_txn) {
     368                 :         22 :                     sbrec_port_binding_set_chassis(binding, chassis_rec);
     369                 :            :                 }
     370                 :            :             }
     371                 :            :         }
     372                 :            :     }
     373                 :       2991 :     add_logical_patch_ports_postprocess(patched_datapaths);
     374                 :            : }
     375                 :            : 
     376                 :            : void
     377                 :       3167 : patch_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
     378                 :            :           const char *chassis_id, struct hmap *local_datapaths,
     379                 :            :           struct hmap *patched_datapaths)
     380                 :            : {
     381         [ +  + ]:       3167 :     if (!ctx->ovs_idl_txn) {
     382                 :        174 :         return;
     383                 :            :     }
     384                 :            : 
     385                 :            :     /* Figure out what patch ports already exist. */
     386                 :       2993 :     struct shash existing_ports = SHASH_INITIALIZER(&existing_ports);
     387                 :            :     const struct ovsrec_port *port;
     388         [ +  + ]:      52641 :     OVSREC_PORT_FOR_EACH (port, ctx->ovs_idl) {
     389         [ +  + ]:      49648 :         if (smap_get(&port->external_ids, "ovn-localnet-port")
     390         [ +  - ]:      48956 :             || smap_get(&port->external_ids, "ovn-l2gateway-port")
     391         [ +  + ]:      48956 :             || smap_get(&port->external_ids, "ovn-l3gateway-port")
     392         [ +  + ]:      48252 :             || smap_get(&port->external_ids, "ovn-logical-patch-port")) {
     393                 :      24498 :             shash_add(&existing_ports, port->name, port);
     394                 :            :         }
     395                 :            :     }
     396                 :            : 
     397                 :            :     /* Create in the database any patch ports that should exist.  Remove from
     398                 :            :      * 'existing_ports' any patch ports that do exist in the database and
     399                 :            :      * should be there. */
     400                 :       2993 :     add_bridge_mappings(ctx, br_int, &existing_ports, local_datapaths,
     401                 :            :                         chassis_id);
     402                 :       2993 :     add_logical_patch_ports(ctx, br_int, chassis_id, &existing_ports,
     403                 :            :                             patched_datapaths);
     404                 :            : 
     405                 :            :     /* Now 'existing_ports' only still contains patch ports that exist in the
     406                 :            :      * database but shouldn't.  Delete them from the database. */
     407                 :            :     struct shash_node *port_node, *port_next_node;
     408 [ +  + ][ -  + ]:       3033 :     SHASH_FOR_EACH_SAFE (port_node, port_next_node, &existing_ports) {
                 [ +  + ]
     409                 :         40 :         struct ovsrec_port *port = port_node->data;
     410                 :         40 :         shash_delete(&existing_ports, port_node);
     411                 :         40 :         remove_port(ctx, port);
     412                 :            :     }
     413                 :       2993 :     shash_destroy(&existing_ports);
     414                 :            : }

Generated by: LCOV version 1.12