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