+/* Copyright (c) 2015 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "gateway.h"
+
+#include "lib/poll-loop.h"
+#include "lib/simap.h"
+#include "lib/sset.h"
+#include "lib/util.h"
+#include "openvswitch/vlog.h"
+#include "ovn/lib/ovn-sb-idl.h"
+#include "vtep/vtep-idl.h"
+#include "ovn-controller-vtep.h"
+
+VLOG_DEFINE_THIS_MODULE(gateway);
+
+/*
+ * Registers the physical switches in vtep to ovnsb as chassis. For each
+ * physical switch in the vtep database, finds all vtep logical switches that
+ * are associated with the physical switch, and updates the corresponding
+ * chassis's 'vtep_logical_switches' column.
+ *
+ */
+
+/* Global revalidation sequence number, incremented at each call to
+ * 'revalidate_gateway()'. */
+static unsigned int gw_reval_seq;
+
+/* Maps all chassis created by the gateway module to their own reval_seq. */
+static struct simap gw_chassis_map = SIMAP_INITIALIZER(&gw_chassis_map);
+
+/* Creates and returns a new instance of 'struct sbrec_chassis'. */
+static const struct sbrec_chassis *
+create_chassis_rec(struct ovsdb_idl_txn *txn, const char *name,
+ const char *encap_ip)
+{
+ const struct sbrec_chassis *chassis_rec;
+ struct sbrec_encap *encap_rec;
+
+ VLOG_INFO("add Chassis row for VTEP physical switch (%s)", name);
+
+ chassis_rec = sbrec_chassis_insert(txn);
+ sbrec_chassis_set_name(chassis_rec, name);
+ encap_rec = sbrec_encap_insert(txn);
+ sbrec_encap_set_type(encap_rec, OVN_SB_ENCAP_TYPE);
+ sbrec_encap_set_ip(encap_rec, encap_ip);
+ sbrec_chassis_set_encaps(chassis_rec, &encap_rec, 1);
+
+ return chassis_rec;
+}
+
+/* Revalidates chassis in ovnsb against vtep database. Creates chassis for
+ * new vtep physical switch. And removes chassis which no longer have
+ * physical switch in vtep.
+ *
+ * xxx: Support multiple tunnel encaps.
+ *
+ * */
+static void
+revalidate_gateway(struct controller_vtep_ctx *ctx)
+{
+ const struct vteprec_physical_switch *pswitch;
+
+ /* Increments the global revalidation sequence number. */
+ gw_reval_seq++;
+
+ ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
+ "ovn-controller-vtep: updating vtep chassis");
+
+ VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
+ const struct sbrec_chassis *chassis_rec;
+ struct simap_node *gw_node;
+ const char *encap_ip;
+
+ encap_ip = pswitch->n_tunnel_ips ? pswitch->tunnel_ips[0] : "";
+ gw_node = simap_find(&gw_chassis_map, pswitch->name);
+ chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
+ if (chassis_rec) {
+ if (!gw_node &&
+ (strcmp(chassis_rec->encaps[0]->type, OVN_SB_ENCAP_TYPE)
+ || strcmp(chassis_rec->encaps[0]->ip, encap_ip))) {
+ VLOG_WARN("Chassis config changing on startup, make sure "
+ "multiple chassis are not configured : %s/%s->%s/%s",
+ chassis_rec->encaps[0]->type,
+ chassis_rec->encaps[0]->ip,
+ OVN_SB_ENCAP_TYPE, encap_ip);
+ }
+ /* Updates chassis's encap if anything changed. */
+ if (strcmp(chassis_rec->encaps[0]->type, OVN_SB_ENCAP_TYPE)) {
+ VLOG_WARN("Chassis for VTEP physical switch (%s) can only have "
+ "encap type \"%s\"", pswitch->name, OVN_SB_ENCAP_TYPE);
+ sbrec_encap_set_type(chassis_rec->encaps[0], OVN_SB_ENCAP_TYPE);
+ }
+ if (strcmp(chassis_rec->encaps[0]->ip, encap_ip)) {
+ sbrec_encap_set_ip(chassis_rec->encaps[0], encap_ip);
+ }
+ } else {
+ if (gw_node) {
+ VLOG_WARN("Chassis for VTEP physical switch (%s) disappears, "
+ "maybe deleted by ovn-sbctl, adding it back",
+ pswitch->name);
+ }
+ /* Creates a new chassis for the VTEP physical switch. */
+ create_chassis_rec(ctx->ovnsb_idl_txn, pswitch->name, encap_ip);
+ }
+ /* Updates or creates the simap node for 'pswitch->name'. */
+ simap_put(&gw_chassis_map, pswitch->name, gw_reval_seq);
+ }
+
+ struct simap_node *iter, *next;
+ /* For 'gw_node' in 'gw_chassis_map' whose data is not
+ * 'gw_reval_seq', it means the corresponding physical switch no
+ * longer exist. So, garbage collects them. */
+ SIMAP_FOR_EACH_SAFE (iter, next, &gw_chassis_map) {
+ if (iter->data != gw_reval_seq) {
+ const struct sbrec_chassis *chassis_rec;
+
+ chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, iter->name);
+ if (chassis_rec) {
+ sbrec_chassis_delete(chassis_rec);
+ }
+ simap_delete(&gw_chassis_map, iter);
+ }
+ }
+}
+
+/* Updates the 'vtep_logical_switches' column in the Chassis table based
+ * on vtep database configuration. */
+static void
+update_vtep_logical_switches(struct controller_vtep_ctx *ctx)
+{
+ const struct vteprec_physical_switch *pswitch;
+
+ ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller-vtep: "
+ "updating chassis's vtep_logical_switches");
+
+ VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
+ const struct sbrec_chassis *chassis_rec =
+ get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
+ struct sset lswitches = SSET_INITIALIZER(&lswitches);
+ size_t i;
+
+ for (i = 0; i < pswitch->n_ports; i++) {
+ const struct vteprec_physical_port *port = pswitch->ports[i];
+ size_t j;
+
+ for (j = 0; j < port->n_vlan_bindings; j++) {
+ const struct vteprec_logical_switch *vtep_lswitch;
+
+ vtep_lswitch = port->value_vlan_bindings[j];
+ /* If not already in 'lswitches', records it. */
+ if (!sset_find(&lswitches, vtep_lswitch->name)) {
+ sset_add(&lswitches, vtep_lswitch->name);
+ }
+ }
+ }
+
+ const char **ls_arr = sset_array(&lswitches);
+ sbrec_chassis_set_vtep_logical_switches(chassis_rec, ls_arr,
+ sset_count(&lswitches));
+ free(ls_arr);
+ sset_destroy(&lswitches);
+ }
+}
+
+\f
+void
+gateway_run(struct controller_vtep_ctx *ctx)
+{
+ if (!ctx->ovnsb_idl_txn) {
+ return;
+ }
+
+ revalidate_gateway(ctx);
+ update_vtep_logical_switches(ctx);
+}
+
+/* Destroys the chassis table entries for vtep physical switches.
+ * Returns true when all done. */
+bool
+gateway_cleanup(struct controller_vtep_ctx *ctx)
+{
+ static bool simap_destroyed = false;
+ const struct vteprec_physical_switch *pswitch;
+
+ if (!ctx->ovnsb_idl_txn) {
+ return false;
+ }
+
+ bool all_done = true;
+ ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller-vtep: "
+ "unregistering vtep chassis");
+ VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
+ const struct sbrec_chassis *chassis_rec;
+
+ chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
+ if (!chassis_rec) {
+ continue;
+ }
+ all_done = false;
+ sbrec_chassis_delete(chassis_rec);
+ }
+ if (!simap_destroyed) {
+ simap_destroy(&gw_chassis_map);
+ simap_destroyed = true;
+ }
+
+ return all_done;
+}