]>
Commit | Line | Data |
---|---|---|
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 | } |