ovn-controller: Handle physical changes correctly
[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 <unistd.h>
18
19 #include "chassis.h"
20
21 #include "lib/smap.h"
22 #include "lib/vswitch-idl.h"
23 #include "openvswitch/dynamic-string.h"
24 #include "openvswitch/vlog.h"
25 #include "ovn/lib/ovn-sb-idl.h"
26 #include "ovn-controller.h"
27
28 VLOG_DEFINE_THIS_MODULE(chassis);
29
30 #ifndef HOST_NAME_MAX
31 /* For windows. */
32 #define HOST_NAME_MAX 255
33 #endif /* HOST_NAME_MAX */
34
35 void
36 chassis_register_ovs_idl(struct ovsdb_idl *ovs_idl)
37 {
38     ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch);
39     ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_external_ids);
40 }
41
42 static const char *
43 pop_tunnel_name(uint32_t *type)
44 {
45     if (*type & GENEVE) {
46         *type &= ~GENEVE;
47         return "geneve";
48     } else if (*type & STT) {
49         *type &= ~STT;
50         return "stt";
51     } else if (*type & VXLAN) {
52         *type &= ~VXLAN;
53         return "vxlan";
54     }
55
56     OVS_NOT_REACHED();
57 }
58
59 static const char *
60 get_bridge_mappings(const struct smap *ext_ids)
61 {
62     const char *bridge_mappings = smap_get(ext_ids, "ovn-bridge-mappings");
63     return bridge_mappings ? bridge_mappings : "";
64 }
65
66 void
67 chassis_run(struct controller_ctx *ctx, const char *chassis_id)
68 {
69     if (!ctx->ovnsb_idl_txn) {
70         return;
71     }
72
73     const struct ovsrec_open_vswitch *cfg;
74     const char *encap_type, *encap_ip;
75     static bool inited = false;
76
77     cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
78     if (!cfg) {
79         VLOG_INFO("No Open_vSwitch row defined.");
80         return;
81     }
82
83     encap_type = smap_get(&cfg->external_ids, "ovn-encap-type");
84     encap_ip = smap_get(&cfg->external_ids, "ovn-encap-ip");
85     if (!encap_type || !encap_ip) {
86         VLOG_INFO("Need to specify an encap type and ip");
87         return;
88     }
89
90     char *tokstr = xstrdup(encap_type);
91     char *save_ptr = NULL;
92     char *token;
93     uint32_t req_tunnels = 0;
94     for (token = strtok_r(tokstr, ",", &save_ptr); token != NULL;
95          token = strtok_r(NULL, ",", &save_ptr)) {
96         uint32_t type = get_tunnel_type(token);
97         if (!type) {
98             VLOG_INFO("Unknown tunnel type: %s", token);
99         }
100         req_tunnels |= type;
101     }
102     free(tokstr);
103
104     const char *hostname = smap_get(&cfg->external_ids, "hostname");
105     char hostname_[HOST_NAME_MAX + 1];
106     if (!hostname || !hostname[0]) {
107         if (gethostname(hostname_, sizeof hostname_)) {
108             hostname_[0] = '\0';
109         }
110         hostname = hostname_;
111     }
112
113     const char *bridge_mappings = get_bridge_mappings(&cfg->external_ids);
114
115     const struct sbrec_chassis *chassis_rec
116         = get_chassis(ctx->ovnsb_idl, chassis_id);
117
118     if (chassis_rec) {
119         if (strcmp(hostname, chassis_rec->hostname)) {
120             sbrec_chassis_set_hostname(chassis_rec, hostname);
121         }
122
123         const char *chassis_bridge_mappings
124             = get_bridge_mappings(&chassis_rec->external_ids);
125         if (strcmp(bridge_mappings, chassis_bridge_mappings)) {
126             struct smap new_ids;
127             smap_clone(&new_ids, &chassis_rec->external_ids);
128             smap_replace(&new_ids, "ovn-bridge-mappings", bridge_mappings);
129             sbrec_chassis_verify_external_ids(chassis_rec);
130             sbrec_chassis_set_external_ids(chassis_rec, &new_ids);
131             smap_destroy(&new_ids);
132         }
133
134         /* Compare desired tunnels against those currently in the database. */
135         uint32_t cur_tunnels = 0;
136         bool same = true;
137         for (int i = 0; i < chassis_rec->n_encaps; i++) {
138             cur_tunnels |= get_tunnel_type(chassis_rec->encaps[i]->type);
139             same = same && !strcmp(chassis_rec->encaps[i]->ip, encap_ip);
140         }
141         same = same && req_tunnels == cur_tunnels;
142
143         if (same) {
144             /* Nothing changed. */
145             inited = true;
146             return;
147         } else if (!inited) {
148             struct ds cur_encaps = DS_EMPTY_INITIALIZER;
149             for (int i = 0; i < chassis_rec->n_encaps; i++) {
150                 ds_put_format(&cur_encaps, "%s,",
151                               chassis_rec->encaps[i]->type);
152             }
153             ds_chomp(&cur_encaps, ',');
154
155             VLOG_WARN("Chassis config changing on startup, make sure "
156                       "multiple chassis are not configured : %s/%s->%s/%s",
157                       ds_cstr(&cur_encaps),
158                       chassis_rec->encaps[0]->ip,
159                       encap_type, encap_ip);
160             ds_destroy(&cur_encaps);
161         }
162     }
163
164     ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
165                               "ovn-controller: registering chassis '%s'",
166                               chassis_id);
167
168     if (!chassis_rec) {
169         struct smap ext_ids = SMAP_CONST1(&ext_ids, "ovn-bridge-mappings",
170                                           bridge_mappings);
171         chassis_rec = sbrec_chassis_insert(ctx->ovnsb_idl_txn);
172         sbrec_chassis_set_name(chassis_rec, chassis_id);
173         sbrec_chassis_set_hostname(chassis_rec, hostname);
174         sbrec_chassis_set_external_ids(chassis_rec, &ext_ids);
175     }
176
177     int n_encaps = count_1bits(req_tunnels);
178     struct sbrec_encap **encaps = xmalloc(n_encaps * sizeof *encaps);
179     for (int i = 0; i < n_encaps; i++) {
180         const char *type = pop_tunnel_name(&req_tunnels);
181
182         encaps[i] = sbrec_encap_insert(ctx->ovnsb_idl_txn);
183
184         sbrec_encap_set_type(encaps[i], type);
185         sbrec_encap_set_ip(encaps[i], encap_ip);
186     }
187
188     sbrec_chassis_set_encaps(chassis_rec, encaps, n_encaps);
189     free(encaps);
190
191     inited = true;
192 }
193
194 /* Returns true if the database is all cleaned up, false if more work is
195  * required. */
196 bool
197 chassis_cleanup(struct controller_ctx *ctx, const char *chassis_id)
198 {
199     if (!chassis_id) {
200         return true;
201     }
202
203     /* Delete Chassis row. */
204     const struct sbrec_chassis *chassis_rec
205         = get_chassis(ctx->ovnsb_idl, chassis_id);
206     if (!chassis_rec) {
207         return true;
208     }
209     if (ctx->ovnsb_idl_txn) {
210         ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
211                                   "ovn-controller: unregistering chassis '%s'",
212                                   chassis_id);
213         sbrec_chassis_delete(chassis_rec);
214     }
215     return false;
216 }