]> git.proxmox.com Git - pve-cluster.git/blob - data/src/confdb.c
7c56119032241e9173671a32b5d768cf53b67dd0
[pve-cluster.git] / data / src / confdb.c
1 /*
2 Copyright (C) 2010-2015 Proxmox Server Solutions GmbH
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Affero General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Affero General Public License for more details.
13
14 You should have received a copy of the GNU Affero General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 Author: Dietmar Maurer <dietmar@proxmox.com>
18
19 */
20
21
22 /* see "man cmap_overview" and "man cmap_keys" */
23
24 #define G_LOG_DOMAIN "confdb"
25
26 #define CLUSTER_KEY "cluster"
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif /* HAVE_CONFIG_H */
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <glib.h>
37
38 #include <corosync/cmap.h>
39
40 #include "cfs-utils.h"
41 #include "loop.h"
42 #include "status.h"
43
44 typedef struct {
45 cmap_handle_t handle;
46 cmap_track_handle_t track_nodelist_handle;
47 cmap_track_handle_t track_version_handle;
48 gboolean changes;
49 } cs_private_t;
50
51 static cs_error_t
52 cmap_read_clusternodes(
53 cmap_handle_t handle,
54 cfs_clinfo_t *clinfo)
55 {
56 cs_error_t result;
57 cmap_iter_handle_t iter;
58
59 result = cmap_iter_init(handle, "nodelist.node.", &iter);
60 if (result != CS_OK) {
61 cfs_critical("cmap_iter_init failed %d", result);
62 return result;
63 }
64
65 cmap_value_types_t type;
66 char key_name[CMAP_KEYNAME_MAXLEN + 1];
67 size_t value_len;
68
69 int last_id = -1;
70 uint32_t nodeid = 0;
71 uint32_t votes = 0;
72 char *name = NULL;
73
74 while ((result = cmap_iter_next(handle, iter, key_name, &value_len, &type)) == CS_OK) {
75 int id;
76 char subkey[CMAP_KEYNAME_MAXLEN + 1];
77 if (sscanf(key_name, "nodelist.node.%d.%s", &id, subkey) != 2) continue;
78
79 if (id != last_id) {
80 if (name && nodeid) {
81 cfs_clnode_t *clnode = cfs_clnode_new(name, nodeid, votes);
82 cfs_clinfo_add_node(clinfo, clnode);
83 }
84 last_id = id;
85 free(name);
86 name = NULL;
87 nodeid = 0;
88 votes = 0;
89 }
90
91 if (strcmp(subkey, "nodeid") == 0) {
92 if ((result = cmap_get_uint32(handle, key_name, &nodeid)) != CS_OK) {
93 cfs_critical("cmap_get %s failed %d", key_name, result);
94 }
95 } else if (strcmp(subkey, "quorum_votes") == 0) {
96 if ((result = cmap_get_uint32(handle, key_name, &votes)) != CS_OK) {
97 cfs_critical("cmap_get %s failed %d", key_name, result);
98 }
99 } else if (strcmp(subkey, "ring0_addr") == 0) {
100 // prefering the 'name' subkey over 'ring0_addr', needed for RRP
101 // and when using a IP address for ring0_addr
102 if (name == NULL &&
103 (result = cmap_get_string(handle, key_name, &name)) != CS_OK) {
104 cfs_critical("cmap_get %s failed %d", key_name, result);
105 }
106 } else if (strcmp(subkey, "name") == 0) {
107 free(name);
108 name = NULL;
109 if ((result = cmap_get_string(handle, key_name, &name)) != CS_OK) {
110 cfs_critical("cmap_get %s failed %d", key_name, result);
111 }
112 }
113 }
114
115 if (name && nodeid) {
116 cfs_clnode_t *clnode = cfs_clnode_new(name, nodeid, votes);
117 cfs_clinfo_add_node(clinfo, clnode);
118 }
119 free(name);
120
121 result = cmap_iter_finalize(handle, iter);
122 if (result != CS_OK) {
123 cfs_critical("cmap_iter_finalize failed %d", result);
124 return result;
125 }
126
127 return result;
128 }
129
130 static cs_error_t
131 cmap_read_config(cmap_handle_t handle)
132 {
133 cs_error_t result;
134
135 uint64_t config_version = 0;
136
137 result = cmap_get_uint64(handle, "totem.config_version", &config_version);
138 if (result != CS_OK) {
139 cfs_critical("cmap_get totem.config_version failed %d", result);
140 // optional, do not throw error
141 }
142
143 char *clustername = NULL;
144 result = cmap_get_string(handle, "totem.cluster_name", &clustername);
145 if (result != CS_OK) {
146 cfs_critical("cmap_get totem.cluster_name failed %d", result);
147 return result;
148 }
149
150 cfs_clinfo_t *clinfo = cfs_clinfo_new(clustername, config_version);
151 g_free(clustername);
152
153 result = cmap_read_clusternodes(handle, clinfo);
154 if (result == CS_OK) {
155 cfs_status_set_clinfo(clinfo);
156 } else {
157 cfs_clinfo_destroy(clinfo);
158 }
159
160 return result;
161 }
162
163 static gboolean
164 service_cmap_finalize(
165 cfs_service_t *service,
166 gpointer context)
167 {
168 g_return_val_if_fail(service != NULL, FALSE);
169 g_return_val_if_fail(context != NULL, FALSE);
170
171 cs_private_t *private = (cs_private_t *)context;
172 cmap_handle_t handle = private->handle;
173 cs_error_t result;
174
175 if (private->track_nodelist_handle) {
176 result = cmap_track_delete(handle, private->track_nodelist_handle);
177 if (result != CS_OK) {
178 cfs_critical("cmap_track_delete nodelist failed: %d", result);
179 }
180 private->track_nodelist_handle = 0;
181 }
182
183 if (private->track_version_handle) {
184 result = cmap_track_delete(handle, private->track_version_handle);
185 if (result != CS_OK) {
186 cfs_critical("cmap_track_delete version failed: %d", result);
187 }
188 private->track_version_handle = 0;
189 }
190
191 result = cmap_finalize(handle);
192 private->handle = 0;
193 if (result != CS_OK) {
194 cfs_critical("cmap_finalize failed: %d", result);
195 return FALSE;
196 }
197
198 return TRUE;
199 }
200
201 static void
202 track_callback(
203 cmap_handle_t cmap_handle,
204 cmap_track_handle_t cmap_track_handle,
205 int32_t event,
206 const char *key_name,
207 struct cmap_notify_value new_value,
208 struct cmap_notify_value old_value,
209 void *context)
210 {
211 g_return_if_fail(context != NULL);
212
213 cs_private_t *private = (cs_private_t *)context;
214
215 cfs_debug("track_callback %s %d\n", key_name, event);
216
217 private->changes = TRUE;
218 }
219
220
221 static int
222 service_cmap_initialize(
223 cfs_service_t *service,
224 gpointer context)
225 {
226 g_return_val_if_fail(service != NULL, FALSE);
227 g_return_val_if_fail(context != NULL, FALSE);
228
229 cs_private_t *private = (cs_private_t *)context;
230
231 // fixme: do not copy (use pointer)
232 cmap_handle_t handle = private->handle;
233 cs_error_t result;
234
235 if (!private->handle) {
236
237 result = cmap_initialize(&handle);
238 if (result != CS_OK) {
239 cfs_critical("cmap_initialize failed: %d", result);
240 private->handle = 0;
241 return -1;
242 }
243
244 result = cmap_context_set(handle, private);
245 if (result != CS_OK) {
246 cfs_critical("cmap_context_set failed: %d", result);
247 cmap_finalize(handle);
248 private->handle = 0;
249 return -1;
250 }
251
252 private->handle = handle;
253 }
254
255
256 result = cmap_track_add(handle, "nodelist.node.",
257 CMAP_TRACK_PREFIX|CMAP_TRACK_ADD|CMAP_TRACK_DELETE|CMAP_TRACK_MODIFY,
258 track_callback, context, &private->track_nodelist_handle);
259
260 if (result == CS_OK) {
261 result = cmap_track_add(handle, "totem.config_version",
262 CMAP_TRACK_ADD|CMAP_TRACK_DELETE|CMAP_TRACK_MODIFY,
263 track_callback, context, &private->track_version_handle);
264 }
265
266 if (result == CS_ERR_LIBRARY || result == CS_ERR_BAD_HANDLE) {
267 cfs_critical("cmap_track_changes failed: %d - closing handle", result);
268 cmap_finalize(handle);
269 private->handle = 0;
270 return -1;
271 } else if (result != CS_OK) {
272 cfs_critical("cmap_track_changes failed: %d - trying again", result);
273 return -1;
274 }
275
276 int cmap_fd = -1;
277 if ((result = cmap_fd_get(handle, &cmap_fd)) != CS_OK) {
278 cfs_critical("confdb_fd_get failed %d - trying again", result);
279 return -1;
280 }
281
282 cmap_read_config(handle);
283
284 return cmap_fd;
285 }
286
287 static gboolean
288 service_cmap_dispatch(
289 cfs_service_t *service,
290 gpointer context)
291 {
292 g_return_val_if_fail(service != NULL, FALSE);
293 g_return_val_if_fail(context != NULL, FALSE);
294
295 cs_private_t *private = (cs_private_t *)context;
296 cmap_handle_t handle = private->handle;
297
298 cs_error_t result;
299
300 private->changes = FALSE;
301 int retries = 0;
302 loop:
303 result = cmap_dispatch(handle, CS_DISPATCH_ALL);
304 if (result == CS_ERR_TRY_AGAIN) {
305 usleep(100000);
306 ++retries;
307 if ((retries % 100) == 0)
308 cfs_message("cmap_dispatch retry %d", retries);
309 goto loop;
310 }
311
312
313 if (result == CS_OK || result == CS_ERR_TRY_AGAIN) {
314
315 if (private->changes) {
316 result = cmap_read_config(handle);
317
318 private->changes = FALSE;
319
320 if (result == CS_OK)
321 return TRUE;
322 }
323 } else {
324 cfs_critical("cmap_dispatch failed: %d", result);
325 }
326
327 cmap_finalize(handle);
328 private->handle = 0;
329 return FALSE;
330 }
331
332 static cfs_service_callbacks_t cfs_confdb_callbacks = {
333 .cfs_service_initialize_fn = service_cmap_initialize,
334 .cfs_service_finalize_fn = service_cmap_finalize,
335 .cfs_service_dispatch_fn = service_cmap_dispatch,
336 };
337
338 cfs_service_t *
339 service_confdb_new(void)
340 {
341 cfs_service_t *service;
342
343 cs_private_t *private = g_new0(cs_private_t, 1);
344 if (!private)
345 return NULL;
346
347 service = cfs_service_new(&cfs_confdb_callbacks, G_LOG_DOMAIN, private);
348
349 return service;
350 }
351
352 void
353 service_confdb_destroy(cfs_service_t *service)
354 {
355 g_return_if_fail(service != NULL);
356
357 cs_private_t *private =
358 (cs_private_t *)cfs_service_get_context(service);
359
360 g_free(private);
361 g_free(service);
362 }