ovn-controller: Support multiple encaps simultaneously.
[cascardo/ovs.git] / ovn / controller / chassis.c
1 /* Copyright (c) 2015 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 "chassis.h"
18
19 #include "lib/dynamic-string.h"
20 #include "lib/vswitch-idl.h"
21 #include "openvswitch/vlog.h"
22 #include "ovn/lib/ovn-sb-idl.h"
23 #include "ovn-controller.h"
24
25 VLOG_DEFINE_THIS_MODULE(chassis);
26
27 void
28 chassis_register_ovs_idl(struct ovsdb_idl *ovs_idl)
29 {
30     ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch);
31     ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_external_ids);
32 }
33
34 static const char *
35 pop_tunnel_name(uint32_t *type)
36 {
37     if (*type & GENEVE) {
38         *type &= ~GENEVE;
39         return "geneve";
40     } else if (*type & STT) {
41         *type &= ~STT;
42         return "stt";
43     } else if (*type & VXLAN) {
44         *type &= ~VXLAN;
45         return "vxlan";
46     }
47
48     OVS_NOT_REACHED();
49 }
50
51 void
52 chassis_run(struct controller_ctx *ctx, const char *chassis_id)
53 {
54     if (!ctx->ovnsb_idl_txn) {
55         return;
56     }
57
58     const struct sbrec_chassis *chassis_rec;
59     const struct ovsrec_open_vswitch *cfg;
60     const char *encap_type, *encap_ip;
61     static bool inited = false;
62
63     chassis_rec = get_chassis(ctx->ovnsb_idl, chassis_id);
64
65     cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
66     if (!cfg) {
67         VLOG_INFO("No Open_vSwitch row defined.");
68         return;
69     }
70
71     encap_type = smap_get(&cfg->external_ids, "ovn-encap-type");
72     encap_ip = smap_get(&cfg->external_ids, "ovn-encap-ip");
73     if (!encap_type || !encap_ip) {
74         VLOG_INFO("Need to specify an encap type and ip");
75         return;
76     }
77
78     char *tokstr = xstrdup(encap_type);
79     char *save_ptr = NULL;
80     char *token;
81     uint32_t req_tunnels = 0;
82     for (token = strtok_r(tokstr, ",", &save_ptr); token != NULL;
83          token = strtok_r(NULL, ",", &save_ptr)) {
84         uint32_t type = get_tunnel_type(token);
85         if (!type) {
86             VLOG_INFO("Unknown tunnel type: %s", token);
87         }
88         req_tunnels |= type;
89     }
90     free(tokstr);
91
92     if (chassis_rec) {
93         /* Compare desired tunnels against those currently in the database. */
94         uint32_t cur_tunnels = 0;
95         bool same = true;
96         for (int i = 0; i < chassis_rec->n_encaps; i++) {
97             cur_tunnels |= get_tunnel_type(chassis_rec->encaps[i]->type);
98             same = same && strcmp(chassis_rec->encaps[i]->ip, encap_ip);
99         }
100         same = same && req_tunnels == cur_tunnels;
101
102         if (same) {
103             /* Nothing changed. */
104             inited = true;
105             return;
106         } else if (!inited) {
107             struct ds cur_encaps = DS_EMPTY_INITIALIZER;
108             for (int i = 0; i < chassis_rec->n_encaps; i++) {
109                 ds_put_format(&cur_encaps, "%s,",
110                               chassis_rec->encaps[i]->type);
111             }
112             ds_chomp(&cur_encaps, ',');
113
114             VLOG_WARN("Chassis config changing on startup, make sure "
115                       "multiple chassis are not configured : %s/%s->%s/%s",
116                       ds_cstr(&cur_encaps),
117                       chassis_rec->encaps[0]->ip,
118                       encap_type, encap_ip);
119             ds_destroy(&cur_encaps);
120         }
121     }
122
123     ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
124                               "ovn-controller: registering chassis '%s'",
125                               chassis_id);
126
127     if (!chassis_rec) {
128         chassis_rec = sbrec_chassis_insert(ctx->ovnsb_idl_txn);
129         sbrec_chassis_set_name(chassis_rec, chassis_id);
130     }
131
132     int n_encaps = count_1bits(req_tunnels);
133     struct sbrec_encap **encaps = xmalloc(n_encaps * sizeof *encaps);
134     for (int i = 0; i < n_encaps; i++) {
135         const char *type = pop_tunnel_name(&req_tunnels);
136
137         encaps[i] = sbrec_encap_insert(ctx->ovnsb_idl_txn);
138
139         sbrec_encap_set_type(encaps[i], type);
140         sbrec_encap_set_ip(encaps[i], encap_ip);
141     }
142
143     sbrec_chassis_set_encaps(chassis_rec, encaps, n_encaps);
144     free(encaps);
145
146     inited = true;
147 }
148
149 /* Returns true if the database is all cleaned up, false if more work is
150  * required. */
151 bool
152 chassis_cleanup(struct controller_ctx *ctx, const char *chassis_id)
153 {
154     if (!chassis_id) {
155         return true;
156     }
157
158     /* Delete Chassis row. */
159     const struct sbrec_chassis *chassis_rec
160         = get_chassis(ctx->ovnsb_idl, chassis_id);
161     if (!chassis_rec) {
162         return true;
163     }
164     if (ctx->ovnsb_idl_txn) {
165         ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
166                                   "ovn-controller: unregistering chassis '%s'",
167                                   chassis_id);
168         sbrec_chassis_delete(chassis_rec);
169     }
170     return false;
171 }