]> git.proxmox.com Git - pve-cluster.git/blame - data/src/status.c
cleanup format strings for cfs_* messages
[pve-cluster.git] / data / src / status.c
CommitLineData
fe000966
DM
1/*
2 Copyright (C) 2010 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#define G_LOG_DOMAIN "status"
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif /* HAVE_CONFIG_H */
26
27#include <stdio.h>
28#include <stdint.h>
29#include <string.h>
30#include <errno.h>
31#include <glib.h>
32#include <sys/syslog.h>
33#include <rrd.h>
34#include <rrd_client.h>
35#include <time.h>
36
37#include "cfs-utils.h"
38#include "status.h"
39#include "logger.h"
40
41#define KVSTORE_CPG_GROUP_NAME "pve_kvstore_v1"
42
43typedef enum {
44 KVSTORE_MESSAGE_UPDATE = 1,
45 KVSTORE_MESSAGE_UPDATE_COMPLETE = 2,
46 KVSTORE_MESSAGE_LOG = 3,
47} kvstore_message_t;
48
49static uint32_t vminfo_version_counter;
50
51typedef struct {
52 uint32_t vmid;
53 char *nodename;
54 int vmtype;
55 uint32_t version;
56} vminfo_t;
57
58typedef struct {
59 char *key;
60 gpointer data;
61 size_t len;
62 uint32_t version;
63} kventry_t;
64
65typedef struct {
66 char *key;
67 gpointer data;
68 size_t len;
69 uint32_t time;
70} rrdentry_t;
71
72typedef struct {
73 char *path;
74 uint32_t version;
75} memdb_change_t;
76
77static memdb_change_t memdb_change_array[] = {
2113d031
DM
78 { .path = "corosync.conf" },
79 { .path = "corosync.conf.new" },
fe000966
DM
80 { .path = "storage.cfg" },
81 { .path = "user.cfg" },
82 { .path = "domains.cfg" },
83 { .path = "priv/shadow.cfg" },
84 { .path = "datacenter.cfg" },
e1735a61 85 { .path = "vzdump.cron" },
5a5417e6
DM
86 { .path = "ha/crm_commands" },
87 { .path = "ha/manager_status" },
88 { .path = "ha/resources.cfg" },
89 { .path = "ha/groups.cfg" },
e9af3eb7 90 { .path = "ha/fence.cfg" },
9d4f69ff 91 { .path = "status.cfg" },
fe000966
DM
92};
93
89fde9ac 94static GMutex mutex;
fe000966
DM
95
96typedef struct {
97 time_t start_time;
98
99 uint32_t quorate;
100
101 cfs_clinfo_t *clinfo;
102 uint32_t clinfo_version;
103
104 GHashTable *vmlist;
105 uint32_t vmlist_version;
106
107 dfsm_t *kvstore;
108 GHashTable *kvhash;
109 GHashTable *rrdhash;
110 GHashTable *iphash;
111
112 GHashTable *memdb_changes;
113
114 clusterlog_t *clusterlog;
115} cfs_status_t;
116
117static cfs_status_t cfs_status;
118
119struct cfs_clnode {
120 char *name;
121 uint32_t nodeid;
122 uint32_t votes;
123 gboolean online;
124 GHashTable *kvhash;
125};
126
127struct cfs_clinfo {
128 char *cluster_name;
129 uint32_t cman_version;
130
131 GHashTable *nodes_byid;
132 GHashTable *nodes_byname;
133};
134
135static guint
136g_int32_hash (gconstpointer v)
137{
138 return *(const uint32_t *) v;
139}
140
141static gboolean
142g_int32_equal (gconstpointer v1,
143 gconstpointer v2)
144{
145 return *((const uint32_t*) v1) == *((const uint32_t*) v2);
146}
147
148static void vminfo_free(vminfo_t *vminfo)
149{
150 g_return_if_fail(vminfo != NULL);
151
152 if (vminfo->nodename)
153 g_free(vminfo->nodename);
154
155
156 g_free(vminfo);
157}
158
159void cfs_clnode_destroy(
160 cfs_clnode_t *clnode)
161{
162 g_return_if_fail(clnode != NULL);
163
164 if (clnode->kvhash)
165 g_hash_table_destroy(clnode->kvhash);
166
167 if (clnode->name)
168 g_free(clnode->name);
169
170 g_free(clnode);
171}
172
173cfs_clnode_t *cfs_clnode_new(
174 const char *name,
175 uint32_t nodeid,
176 uint32_t votes)
177{
178 g_return_val_if_fail(name != NULL, NULL);
179
180 cfs_clnode_t *clnode = g_new0(cfs_clnode_t, 1);
181 if (!clnode)
182 return NULL;
183
184 clnode->name = g_strdup(name);
185 clnode->nodeid = nodeid;
186 clnode->votes = votes;
187
188 return clnode;
189}
190
191gboolean cfs_clinfo_destroy(
192 cfs_clinfo_t *clinfo)
193{
194 g_return_val_if_fail(clinfo != NULL, FALSE);
195
196 if (clinfo->cluster_name)
197 g_free(clinfo->cluster_name);
198
199 if (clinfo->nodes_byname)
200 g_hash_table_destroy(clinfo->nodes_byname);
201
202 if (clinfo->nodes_byid)
203 g_hash_table_destroy(clinfo->nodes_byid);
204
205 g_free(clinfo);
206
207 return TRUE;
208}
209
210cfs_clinfo_t *cfs_clinfo_new(
211 const char *cluster_name,
212 uint32_t cman_version)
213{
214 g_return_val_if_fail(cluster_name != NULL, NULL);
215
216 cfs_clinfo_t *clinfo = g_new0(cfs_clinfo_t, 1);
217 if (!clinfo)
218 return NULL;
219
220 clinfo->cluster_name = g_strdup(cluster_name);
221 clinfo->cman_version = cman_version;
222
223 if (!(clinfo->nodes_byid = g_hash_table_new_full(
224 g_int32_hash, g_int32_equal, NULL,
225 (GDestroyNotify)cfs_clnode_destroy)))
226 goto fail;
227
228 if (!(clinfo->nodes_byname = g_hash_table_new(g_str_hash, g_str_equal)))
229 goto fail;
230
231 return clinfo;
232
233fail:
234 cfs_clinfo_destroy(clinfo);
235
236 return NULL;
237}
238
239gboolean cfs_clinfo_add_node(
240 cfs_clinfo_t *clinfo,
241 cfs_clnode_t *clnode)
242{
243 g_return_val_if_fail(clinfo != NULL, FALSE);
244 g_return_val_if_fail(clnode != NULL, FALSE);
245
246 g_hash_table_replace(clinfo->nodes_byid, &clnode->nodeid, clnode);
247 g_hash_table_replace(clinfo->nodes_byname, clnode->name, clnode);
248
249 return TRUE;
250}
251
252int
253cfs_create_memberlist_msg(
254 GString *str)
255{
256 g_return_val_if_fail(str != NULL, -EINVAL);
257
89fde9ac 258 g_mutex_lock (&mutex);
fe000966
DM
259
260 g_string_append_printf(str,"{\n");
261
262 guint nodecount = 0;
263
264 cfs_clinfo_t *clinfo = cfs_status.clinfo;
265
266 if (clinfo && clinfo->nodes_byid)
267 nodecount = g_hash_table_size(clinfo->nodes_byid);
268
269 if (nodecount) {
270 g_string_append_printf(str, "\"nodename\": \"%s\",\n", cfs.nodename);
271 g_string_append_printf(str, "\"version\": %u,\n", cfs_status.clinfo_version);
272
273 g_string_append_printf(str, "\"cluster\": { ");
274 g_string_append_printf(str, "\"name\": \"%s\", \"version\": %d, "
275 "\"nodes\": %d, \"quorate\": %d ",
276 clinfo->cluster_name, clinfo->cman_version,
277 nodecount, cfs_status.quorate);
278
279 g_string_append_printf(str,"},\n");
280 g_string_append_printf(str,"\"nodelist\": {\n");
281
282 GHashTable *ht = clinfo->nodes_byid;
283 GHashTableIter iter;
284 gpointer key, value;
285
286 g_hash_table_iter_init (&iter, ht);
287
288 int i = 0;
289 while (g_hash_table_iter_next (&iter, &key, &value)) {
290 cfs_clnode_t *node = (cfs_clnode_t *)value;
291 if (i) g_string_append_printf(str, ",\n");
292 i++;
293
294 g_string_append_printf(str, " \"%s\": { \"id\": %d, \"online\": %d",
295 node->name, node->nodeid, node->online);
296
297
298 char *ip = (char *)g_hash_table_lookup(cfs_status.iphash, node->name);
299 if (ip) {
300 g_string_append_printf(str, ", \"ip\": \"%s\"", ip);
301 }
302
303 g_string_append_printf(str, "}");
304
305 }
306 g_string_append_printf(str,"\n }\n");
307 } else {
308 g_string_append_printf(str, "\"nodename\": \"%s\",\n", cfs.nodename);
309 g_string_append_printf(str, "\"version\": %u\n", cfs_status.clinfo_version);
310 }
311
312 g_string_append_printf(str,"}\n");
313
89fde9ac 314 g_mutex_unlock (&mutex);
fe000966
DM
315
316 return 0;
317}
318
319static void
320kventry_free(kventry_t *entry)
321{
322 g_return_if_fail(entry != NULL);
323
324 g_free(entry->key);
325 g_free(entry->data);
326 g_free(entry);
327}
328
329static GHashTable *
330kventry_hash_new(void)
331{
332 return g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
333 (GDestroyNotify)kventry_free);
334}
335
336static void
337rrdentry_free(rrdentry_t *entry)
338{
339 g_return_if_fail(entry != NULL);
340
341 g_free(entry->key);
342 g_free(entry->data);
343 g_free(entry);
344}
345
346static GHashTable *
347rrdentry_hash_new(void)
348{
349 return g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
350 (GDestroyNotify)rrdentry_free);
351}
352
353void
354cfs_cluster_log_dump(GString *str, const char *user, guint max_entries)
355{
356 clusterlog_dump(cfs_status.clusterlog, str, user, max_entries);
357}
358
359void
360cfs_cluster_log(clog_entry_t *entry)
361{
362 g_return_if_fail(entry != NULL);
363
364 clusterlog_insert(cfs_status.clusterlog, entry);
365
366 if (cfs_status.kvstore) {
367 struct iovec iov[1];
368 iov[0].iov_base = (char *)entry;
369 iov[0].iov_len = clog_entry_size(entry);
370
af2e9dd4
DM
371 if (dfsm_is_initialized(cfs_status.kvstore))
372 dfsm_send_message(cfs_status.kvstore, KVSTORE_MESSAGE_LOG, iov, 1);
fe000966
DM
373 }
374}
375
376void cfs_status_init(void)
377{
89fde9ac 378 g_mutex_lock (&mutex);
fe000966
DM
379
380 cfs_status.start_time = time(NULL);
381
382 cfs_status.vmlist = vmlist_hash_new();
383
384 cfs_status.kvhash = kventry_hash_new();
385
386 cfs_status.rrdhash = rrdentry_hash_new();
387
388 cfs_status.iphash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
389
390 cfs_status.memdb_changes = g_hash_table_new(g_str_hash, g_str_equal);
391
392 for (int i = 0; i < G_N_ELEMENTS(memdb_change_array); i++) {
393 g_hash_table_replace(cfs_status.memdb_changes,
394 memdb_change_array[i].path,
395 &memdb_change_array[i]);
396 }
397
398 cfs_status.clusterlog = clusterlog_new();
399
400 // fixme:
401 clusterlog_add(cfs_status.clusterlog, "root", "cluster", getpid(),
402 LOG_INFO, "starting cluster log");
403
89fde9ac 404 g_mutex_unlock (&mutex);
fe000966
DM
405}
406
407void cfs_status_cleanup(void)
408{
89fde9ac 409 g_mutex_lock (&mutex);
fe000966
DM
410
411 cfs_status.clinfo_version++;
412
413 if (cfs_status.clinfo) {
414 cfs_clinfo_destroy(cfs_status.clinfo);
415 cfs_status.clinfo = NULL;
416 }
417
418 if (cfs_status.vmlist) {
419 g_hash_table_destroy(cfs_status.vmlist);
420 cfs_status.vmlist = NULL;
421 }
422
423 if (cfs_status.kvhash) {
424 g_hash_table_destroy(cfs_status.kvhash);
425 cfs_status.kvhash = NULL;
426 }
427
428 if (cfs_status.rrdhash) {
429 g_hash_table_destroy(cfs_status.rrdhash);
430 cfs_status.rrdhash = NULL;
431 }
432
433 if (cfs_status.iphash) {
434 g_hash_table_destroy(cfs_status.iphash);
435 cfs_status.iphash = NULL;
436 }
437
438 if (cfs_status.clusterlog)
439 clusterlog_destroy(cfs_status.clusterlog);
440
89fde9ac 441 g_mutex_unlock (&mutex);
fe000966
DM
442}
443
444void cfs_status_set_clinfo(
445 cfs_clinfo_t *clinfo)
446{
447 g_return_if_fail(clinfo != NULL);
448
89fde9ac 449 g_mutex_lock (&mutex);
fe000966
DM
450
451 cfs_status.clinfo_version++;
452
453 cfs_clinfo_t *old = cfs_status.clinfo;
454
455 cfs_status.clinfo = clinfo;
456
457 cfs_message("update cluster info (cluster name %s, version = %d)",
458 clinfo->cluster_name, clinfo->cman_version);
459
460
461 if (old && old->nodes_byid && clinfo->nodes_byid) {
462 /* copy kvstore */
463 GHashTable *ht = clinfo->nodes_byid;
464 GHashTableIter iter;
465 gpointer key, value;
466
467 g_hash_table_iter_init (&iter, ht);
468
469 while (g_hash_table_iter_next (&iter, &key, &value)) {
470 cfs_clnode_t *node = (cfs_clnode_t *)value;
471 cfs_clnode_t *oldnode;
469d3acb 472 if ((oldnode = g_hash_table_lookup(old->nodes_byid, key))) {
fe000966
DM
473 node->online = oldnode->online;
474 node->kvhash = oldnode->kvhash;
475 oldnode->kvhash = NULL;
476 }
477 }
478
479 }
480
481 if (old)
482 cfs_clinfo_destroy(old);
483
484
89fde9ac 485 g_mutex_unlock (&mutex);
fe000966
DM
486}
487
488static void
489dump_kvstore_versions(
490 GString *str,
491 GHashTable *kvhash,
492 const char *nodename)
493{
494 g_return_if_fail(kvhash != NULL);
495 g_return_if_fail(str != NULL);
496 g_return_if_fail(nodename != NULL);
497
498 GHashTable *ht = kvhash;
499 GHashTableIter iter;
500 gpointer key, value;
501
502 g_string_append_printf(str, "\"%s\": {\n", nodename);
503
504 g_hash_table_iter_init (&iter, ht);
505
506 int i = 0;
507 while (g_hash_table_iter_next (&iter, &key, &value)) {
508 kventry_t *entry = (kventry_t *)value;
509 if (i) g_string_append_printf(str, ",\n");
510 i++;
511 g_string_append_printf(str,"\"%s\": %u", entry->key, entry->version);
512 }
513
514 g_string_append_printf(str, "}\n");
515}
516
517int
518cfs_create_version_msg(GString *str)
519{
520 g_return_val_if_fail(str != NULL, -EINVAL);
521
89fde9ac 522 g_mutex_lock (&mutex);
fe000966
DM
523
524 g_string_append_printf(str,"{\n");
525
526 g_string_append_printf(str, "\"starttime\": %lu,\n", (unsigned long)cfs_status.start_time);
527
528 g_string_append_printf(str, "\"clinfo\": %u,\n", cfs_status.clinfo_version);
529
530 g_string_append_printf(str, "\"vmlist\": %u,\n", cfs_status.vmlist_version);
531
532 for (int i = 0; i < G_N_ELEMENTS(memdb_change_array); i++) {
533 g_string_append_printf(str, "\"%s\": %u,\n",
534 memdb_change_array[i].path,
535 memdb_change_array[i].version);
536 }
537
538 g_string_append_printf(str, "\"kvstore\": {\n");
539
540 dump_kvstore_versions(str, cfs_status.kvhash, cfs.nodename);
541
542 cfs_clinfo_t *clinfo = cfs_status.clinfo;
543
544 if (clinfo && clinfo->nodes_byid) {
545 GHashTable *ht = clinfo->nodes_byid;
546 GHashTableIter iter;
547 gpointer key, value;
548
549 g_hash_table_iter_init (&iter, ht);
550
551 while (g_hash_table_iter_next (&iter, &key, &value)) {
552 cfs_clnode_t *node = (cfs_clnode_t *)value;
553 if (!node->kvhash)
554 continue;
555 g_string_append_printf(str, ",\n");
556 dump_kvstore_versions(str, node->kvhash, node->name);
557 }
558 }
559
560 g_string_append_printf(str,"}\n");
561
562 g_string_append_printf(str,"}\n");
563
89fde9ac 564 g_mutex_unlock (&mutex);
fe000966
DM
565
566 return 0;
567}
568
569GHashTable *
570vmlist_hash_new(void)
571{
572 return g_hash_table_new_full(g_int_hash, g_int_equal, NULL,
573 (GDestroyNotify)vminfo_free);
574}
575
576gboolean
577vmlist_hash_insert_vm(
578 GHashTable *vmlist,
579 int vmtype,
580 guint32 vmid,
581 const char *nodename,
582 gboolean replace)
583{
584 g_return_val_if_fail(vmlist != NULL, FALSE);
585 g_return_val_if_fail(nodename != NULL, FALSE);
586 g_return_val_if_fail(vmid != 0, FALSE);
7f66b436
DM
587 g_return_val_if_fail(vmtype == VMTYPE_QEMU || vmtype == VMTYPE_OPENVZ ||
588 vmtype == VMTYPE_LXC, FALSE);
fe000966
DM
589
590 if (!replace && g_hash_table_lookup(vmlist, &vmid)) {
591 cfs_critical("detected duplicate VMID %d", vmid);
592 return FALSE;
593 }
594
595 vminfo_t *vminfo = g_new0(vminfo_t, 1);
596
597 vminfo->vmid = vmid;
598 vminfo->vmtype = vmtype;
599 vminfo->nodename = g_strdup(nodename);
600
601 vminfo->version = ++vminfo_version_counter;
602
603 g_hash_table_replace(vmlist, &vminfo->vmid, vminfo);
604
605 return TRUE;
606}
607
608void
609vmlist_register_vm(
610 int vmtype,
611 guint32 vmid,
612 const char *nodename)
613{
614 g_return_if_fail(cfs_status.vmlist != NULL);
615 g_return_if_fail(nodename != NULL);
616 g_return_if_fail(vmid != 0);
7f66b436
DM
617 g_return_if_fail(vmtype == VMTYPE_QEMU || vmtype == VMTYPE_OPENVZ ||
618 vmtype == VMTYPE_LXC);
fe000966
DM
619
620 cfs_debug("vmlist_register_vm: %s/%u %d", nodename, vmid, vmtype);
621
89fde9ac 622 g_mutex_lock (&mutex);
fe000966
DM
623
624 cfs_status.vmlist_version++;
625
626 vmlist_hash_insert_vm(cfs_status.vmlist, vmtype, vmid, nodename, TRUE);
627
89fde9ac 628 g_mutex_unlock (&mutex);
fe000966
DM
629}
630
631gboolean
632vmlist_different_vm_exists(
633 int vmtype,
634 guint32 vmid,
635 const char *nodename)
636{
637 g_return_val_if_fail(cfs_status.vmlist != NULL, FALSE);
638 g_return_val_if_fail(vmid != 0, FALSE);
639
640 gboolean res = FALSE;
641
89fde9ac 642 g_mutex_lock (&mutex);
fe000966
DM
643
644 vminfo_t *vminfo;
645 if ((vminfo = (vminfo_t *)g_hash_table_lookup(cfs_status.vmlist, &vmid))) {
646 if (!(vminfo->vmtype == vmtype && strcmp(vminfo->nodename, nodename) == 0))
647 res = TRUE;
648 }
89fde9ac 649 g_mutex_unlock (&mutex);
fe000966
DM
650
651 return res;
652}
653
654gboolean
655vmlist_vm_exists(
656 guint32 vmid)
657{
658 g_return_val_if_fail(cfs_status.vmlist != NULL, FALSE);
659 g_return_val_if_fail(vmid != 0, FALSE);
660
89fde9ac 661 g_mutex_lock (&mutex);
fe000966
DM
662
663 gpointer res = g_hash_table_lookup(cfs_status.vmlist, &vmid);
664
89fde9ac 665 g_mutex_unlock (&mutex);
fe000966
DM
666
667 return res != NULL;
668}
669
670void
671vmlist_delete_vm(
672 guint32 vmid)
673{
674 g_return_if_fail(cfs_status.vmlist != NULL);
675 g_return_if_fail(vmid != 0);
676
89fde9ac 677 g_mutex_lock (&mutex);
fe000966
DM
678
679 cfs_status.vmlist_version++;
680
681 g_hash_table_remove(cfs_status.vmlist, &vmid);
682
89fde9ac 683 g_mutex_unlock (&mutex);
fe000966
DM
684}
685
686void cfs_status_set_vmlist(
687 GHashTable *vmlist)
688{
689 g_return_if_fail(vmlist != NULL);
690
89fde9ac 691 g_mutex_lock (&mutex);
fe000966
DM
692
693 cfs_status.vmlist_version++;
694
695 if (cfs_status.vmlist)
696 g_hash_table_destroy(cfs_status.vmlist);
697
698 cfs_status.vmlist = vmlist;
699
89fde9ac 700 g_mutex_unlock (&mutex);
fe000966
DM
701}
702
703int
704cfs_create_vmlist_msg(GString *str)
705{
706 g_return_val_if_fail(cfs_status.vmlist != NULL, -EINVAL);
707 g_return_val_if_fail(str != NULL, -EINVAL);
708
89fde9ac 709 g_mutex_lock (&mutex);
fe000966
DM
710
711 g_string_append_printf(str,"{\n");
712
713 GHashTable *ht = cfs_status.vmlist;
714
715 guint count = g_hash_table_size(ht);
716
717 if (!count) {
718 g_string_append_printf(str,"\"version\": %u\n", cfs_status.vmlist_version);
719 } else {
720 g_string_append_printf(str,"\"version\": %u,\n", cfs_status.vmlist_version);
721
722 g_string_append_printf(str,"\"ids\": {\n");
723
724 GHashTableIter iter;
725 gpointer key, value;
726
727 g_hash_table_iter_init (&iter, ht);
728
729 int first = 1;
730 while (g_hash_table_iter_next (&iter, &key, &value)) {
731 vminfo_t *vminfo = (vminfo_t *)value;
732 char *type;
733 if (vminfo->vmtype == VMTYPE_QEMU) {
734 type = "qemu";
735 } else if (vminfo->vmtype == VMTYPE_OPENVZ) {
736 type = "openvz";
7f66b436
DM
737 } else if (vminfo->vmtype == VMTYPE_LXC) {
738 type = "lxc";
fe000966
DM
739 } else {
740 type = "unknown";
741 }
742
743 if (!first)
744 g_string_append_printf(str, ",\n");
745 first = 0;
746
747 g_string_append_printf(str,"\"%u\": { \"node\": \"%s\", \"type\": \"%s\", \"version\": %u }",
748 vminfo->vmid, vminfo->nodename, type, vminfo->version);
749 }
750
751 g_string_append_printf(str,"}\n");
752 }
753 g_string_append_printf(str,"\n}\n");
754
89fde9ac 755 g_mutex_unlock (&mutex);
fe000966
DM
756
757 return 0;
758}
759
760void
761record_memdb_change(const char *path)
762{
763 g_return_if_fail(cfs_status.memdb_changes != 0);
764
765 memdb_change_t *ce;
766
767 if ((ce = (memdb_change_t *)g_hash_table_lookup(cfs_status.memdb_changes, path))) {
768 ce->version++;
769 }
770}
771
772void
773record_memdb_reload(void)
774{
775 for (int i = 0; i < G_N_ELEMENTS(memdb_change_array); i++) {
776 memdb_change_array[i].version++;
777 }
778}
779
780static gboolean
781kventry_hash_set(
782 GHashTable *kvhash,
783 const char *key,
784 gconstpointer data,
785 size_t len)
786{
787 g_return_val_if_fail(kvhash != NULL, FALSE);
788 g_return_val_if_fail(key != NULL, FALSE);
789 g_return_val_if_fail(data != NULL, FALSE);
790
791 kventry_t *entry;
792 if ((entry = (kventry_t *)g_hash_table_lookup(kvhash, key))) {
793 g_free(entry->data);
794 entry->data = g_memdup(data, len);
795 entry->len = len;
796 entry->version++;
797 } else {
798 kventry_t *entry = g_new0(kventry_t, 1);
799
800 entry->key = g_strdup(key);
801 entry->data = g_memdup(data, len);
802 entry->len = len;
803
804 g_hash_table_replace(kvhash, entry->key, entry);
805 }
806
807 return TRUE;
808}
809
810static const char *rrd_def_node[] = {
811 "DS:loadavg:GAUGE:120:0:U",
812 "DS:maxcpu:GAUGE:120:0:U",
813 "DS:cpu:GAUGE:120:0:U",
814 "DS:iowait:GAUGE:120:0:U",
815 "DS:memtotal:GAUGE:120:0:U",
816 "DS:memused:GAUGE:120:0:U",
817 "DS:swaptotal:GAUGE:120:0:U",
818 "DS:swapused:GAUGE:120:0:U",
819 "DS:roottotal:GAUGE:120:0:U",
820 "DS:rootused:GAUGE:120:0:U",
764296f1
DM
821 "DS:netin:DERIVE:120:0:U",
822 "DS:netout:DERIVE:120:0:U",
fe000966
DM
823
824 "RRA:AVERAGE:0.5:1:70", // 1 min avg - one hour
825 "RRA:AVERAGE:0.5:30:70", // 30 min avg - one day
826 "RRA:AVERAGE:0.5:180:70", // 3 hour avg - one week
827 "RRA:AVERAGE:0.5:720:70", // 12 hour avg - one month
828 "RRA:AVERAGE:0.5:10080:70", // 7 day avg - ony year
829
830 "RRA:MAX:0.5:1:70", // 1 min max - one hour
831 "RRA:MAX:0.5:30:70", // 30 min max - one day
832 "RRA:MAX:0.5:180:70", // 3 hour max - one week
833 "RRA:MAX:0.5:720:70", // 12 hour max - one month
834 "RRA:MAX:0.5:10080:70", // 7 day max - ony year
835 NULL,
836};
837
838static const char *rrd_def_vm[] = {
839 "DS:maxcpu:GAUGE:120:0:U",
840 "DS:cpu:GAUGE:120:0:U",
841 "DS:maxmem:GAUGE:120:0:U",
842 "DS:mem:GAUGE:120:0:U",
843 "DS:maxdisk:GAUGE:120:0:U",
844 "DS:disk:GAUGE:120:0:U",
764296f1
DM
845 "DS:netin:DERIVE:120:0:U",
846 "DS:netout:DERIVE:120:0:U",
847 "DS:diskread:DERIVE:120:0:U",
848 "DS:diskwrite:DERIVE:120:0:U",
fe000966
DM
849
850 "RRA:AVERAGE:0.5:1:70", // 1 min avg - one hour
851 "RRA:AVERAGE:0.5:30:70", // 30 min avg - one day
852 "RRA:AVERAGE:0.5:180:70", // 3 hour avg - one week
853 "RRA:AVERAGE:0.5:720:70", // 12 hour avg - one month
854 "RRA:AVERAGE:0.5:10080:70", // 7 day avg - ony year
855
856 "RRA:MAX:0.5:1:70", // 1 min max - one hour
857 "RRA:MAX:0.5:30:70", // 30 min max - one day
858 "RRA:MAX:0.5:180:70", // 3 hour max - one week
859 "RRA:MAX:0.5:720:70", // 12 hour max - one month
860 "RRA:MAX:0.5:10080:70", // 7 day max - ony year
861 NULL,
862};
863
864static const char *rrd_def_storage[] = {
865 "DS:total:GAUGE:120:0:U",
866 "DS:used:GAUGE:120:0:U",
867
868 "RRA:AVERAGE:0.5:1:70", // 1 min avg - one hour
869 "RRA:AVERAGE:0.5:30:70", // 30 min avg - one day
870 "RRA:AVERAGE:0.5:180:70", // 3 hour avg - one week
871 "RRA:AVERAGE:0.5:720:70", // 12 hour avg - one month
872 "RRA:AVERAGE:0.5:10080:70", // 7 day avg - ony year
873
874 "RRA:MAX:0.5:1:70", // 1 min max - one hour
875 "RRA:MAX:0.5:30:70", // 30 min max - one day
876 "RRA:MAX:0.5:180:70", // 3 hour max - one week
877 "RRA:MAX:0.5:720:70", // 12 hour max - one month
878 "RRA:MAX:0.5:10080:70", // 7 day max - ony year
879 NULL,
880};
881
882#define RRDDIR "/var/lib/rrdcached/db"
883
884static void
885create_rrd_file(
886 const char *filename,
887 int argcount,
888 const char *rrddef[])
889{
890 /* start at day boundary */
891 time_t ctime;
892 time(&ctime);
893 struct tm *ltm = localtime(&ctime);
894 ltm->tm_sec = 0;
895 ltm->tm_min = 0;
896 ltm->tm_hour = 0;
897
898 rrd_clear_error();
899 if (rrd_create_r(filename, 60, timelocal(ltm), argcount, rrddef)) {
900 cfs_message("RRD create error %s: %s", filename, rrd_get_error());
901 }
902}
903
904static inline const char *
905rrd_skip_data(
906 const char *data,
907 int count)
908{
909 int found = 0;
910 while (*data && found < count) {
911 if (*data++ == ':')
912 found++;
913 }
914 return data;
915}
916
917static void
918update_rrd_data(
919 const char *key,
920 gconstpointer data,
921 size_t len)
922{
923 g_return_if_fail(key != NULL);
924 g_return_if_fail(data != NULL);
925 g_return_if_fail(len > 0);
926 g_return_if_fail(len < 4096);
927
928 static const char *rrdcsock = "unix:/var/run/rrdcached.sock";
929
930 int use_daemon = 1;
931 if (rrdc_connect(rrdcsock) != 0)
932 use_daemon = 0;
933
ba9dcfc1 934 char *filename = NULL;
fe000966
DM
935
936 int skip = 0;
937
938 if (strncmp(key, "pve2-node/", 10) == 0) {
939 const char *node = key + 10;
940
f9c865a8 941 skip = 2;
fe000966
DM
942
943 if (strchr(node, '/') != NULL)
944 goto keyerror;
945
946 if (strlen(node) < 1)
947 goto keyerror;
948
ba9dcfc1
DM
949 filename = g_strdup_printf(RRDDIR "/%s", key);
950
fe000966
DM
951 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
952
953 mkdir(RRDDIR "/pve2-node", 0755);
954 int argcount = sizeof(rrd_def_node)/sizeof(void*) - 1;
955 create_rrd_file(filename, argcount, rrd_def_node);
956 }
957
ba9dcfc1
DM
958 } else if ((strncmp(key, "pve2-vm/", 8) == 0) ||
959 (strncmp(key, "pve2.3-vm/", 10) == 0)) {
960 const char *vmid;
fe000966 961
ba9dcfc1
DM
962 if (strncmp(key, "pve2-vm/", 8) == 0) {
963 vmid = key + 8;
964 skip = 2;
965 } else {
966 vmid = key + 10;
94e4cba3 967 skip = 4;
ba9dcfc1 968 }
fe000966
DM
969
970 if (strchr(vmid, '/') != NULL)
971 goto keyerror;
972
973 if (strlen(vmid) < 1)
974 goto keyerror;
975
ba9dcfc1
DM
976 filename = g_strdup_printf(RRDDIR "/%s/%s", "pve2-vm", vmid);
977
fe000966
DM
978 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
979
980 mkdir(RRDDIR "/pve2-vm", 0755);
981 int argcount = sizeof(rrd_def_vm)/sizeof(void*) - 1;
982 create_rrd_file(filename, argcount, rrd_def_vm);
983 }
984
985 } else if (strncmp(key, "pve2-storage/", 13) == 0) {
986 const char *node = key + 13;
987
988 const char *storage = node;
989 while (*storage && *storage != '/')
990 storage++;
991
992 if (*storage != '/' || ((storage - node) < 1))
993 goto keyerror;
994
995 storage++;
996
997 if (strchr(storage, '/') != NULL)
998 goto keyerror;
999
1000 if (strlen(storage) < 1)
1001 goto keyerror;
1002
ba9dcfc1
DM
1003 filename = g_strdup_printf(RRDDIR "/%s", key);
1004
fe000966
DM
1005 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
1006
1007 mkdir(RRDDIR "/pve2-storage", 0755);
1008
1009 char *dir = g_path_get_dirname(filename);
1010 mkdir(dir, 0755);
1011 g_free(dir);
1012
1013 int argcount = sizeof(rrd_def_storage)/sizeof(void*) - 1;
1014 create_rrd_file(filename, argcount, rrd_def_storage);
1015 }
1016
1017 } else {
1018 goto keyerror;
1019 }
1020
1021 const char *dp = skip ? rrd_skip_data(data, skip) : data;
1022
1023 const char *update_args[] = { dp, NULL };
1024
1025 if (use_daemon) {
1026 int status;
1027 if ((status = rrdc_update(filename, 1, update_args)) != 0) {
1028 cfs_message("RRDC update error %s: %d", filename, status);
1029 rrdc_disconnect();
1030 rrd_clear_error();
1031 if (rrd_update_r(filename, NULL, 1, update_args) != 0) {
1032 cfs_message("RRD update error %s: %s", filename, rrd_get_error());
1033 }
1034 }
1035
1036 } else {
1037 rrd_clear_error();
1038 if (rrd_update_r(filename, NULL, 1, update_args) != 0) {
1039 cfs_message("RRD update error %s: %s", filename, rrd_get_error());
1040 }
1041 }
1042
1043ret:
ba9dcfc1
DM
1044 if (filename)
1045 g_free(filename);
1046
fe000966
DM
1047 return;
1048
1049keyerror:
1050 cfs_critical("RRD update error: unknown/wrong key %s", key);
1051 goto ret;
1052}
1053
1054static gboolean
1055rrd_entry_is_old(
1056 gpointer key,
1057 gpointer value,
1058 gpointer user_data)
1059{
1060 rrdentry_t *entry = (rrdentry_t *)value;
1061 uint32_t ctime = GPOINTER_TO_UINT(user_data);
1062
1063 int diff = ctime - entry->time;
1064
1065 /* remove everything older than 5 minutes */
1066 int expire = 60*5;
1067
1068 return (diff > expire) ? TRUE : FALSE;
1069}
1070
1071static char *rrd_dump_buf = NULL;
1072static time_t rrd_dump_last = 0;
1073
1074void
1075cfs_rrd_dump(GString *str)
1076{
1077 time_t ctime;
1078 time(&ctime);
1079
1080 if (rrd_dump_buf && (ctime - rrd_dump_last) < 2) {
1081 g_string_assign(str, rrd_dump_buf);
1082 return;
1083 }
1084
1085 /* remove old data */
1086 g_hash_table_foreach_remove(cfs_status.rrdhash, rrd_entry_is_old,
1087 GUINT_TO_POINTER(ctime));
1088
1089 g_string_set_size(str, 0);
1090
1091 GHashTableIter iter;
1092 gpointer key, value;
1093
1094 g_hash_table_iter_init (&iter, cfs_status.rrdhash);
1095
1096 while (g_hash_table_iter_next (&iter, &key, &value)) {
1097 rrdentry_t *entry = (rrdentry_t *)value;
1098 g_string_append(str, key);
1099 g_string_append(str, ":");
1100 g_string_append(str, entry->data);
1101 g_string_append(str, "\n");
1102 }
1103
1104 g_string_append_c(str, 0); // never return undef
1105
1106 rrd_dump_last = ctime;
1107 if (rrd_dump_buf)
1108 g_free(rrd_dump_buf);
1109 rrd_dump_buf = g_strdup(str->str);
1110}
1111
1112static gboolean
1113nodeip_hash_set(
1114 GHashTable *iphash,
1115 const char *nodename,
1116 const char *ip,
1117 size_t len)
1118{
1119 g_return_val_if_fail(iphash != NULL, FALSE);
1120 g_return_val_if_fail(nodename != NULL, FALSE);
1121 g_return_val_if_fail(ip != NULL, FALSE);
1122 g_return_val_if_fail(len > 0, FALSE);
1123 g_return_val_if_fail(len < 256, FALSE);
1124 g_return_val_if_fail(ip[len-1] == 0, FALSE);
1125
1126 char *oldip = (char *)g_hash_table_lookup(iphash, nodename);
1127
1128 if (!oldip || (strcmp(oldip, ip) != 0)) {
1129 cfs_status.clinfo_version++;
1130 g_hash_table_replace(iphash, g_strdup(nodename), g_strdup(ip));
1131 }
1132
1133 return TRUE;
1134}
1135
1136static gboolean
1137rrdentry_hash_set(
1138 GHashTable *rrdhash,
1139 const char *key,
1140 const char *data,
1141 size_t len)
1142{
1143 g_return_val_if_fail(rrdhash != NULL, FALSE);
1144 g_return_val_if_fail(key != NULL, FALSE);
1145 g_return_val_if_fail(data != NULL, FALSE);
1146 g_return_val_if_fail(len > 0, FALSE);
1147 g_return_val_if_fail(len < 4096, FALSE);
1148 g_return_val_if_fail(data[len-1] == 0, FALSE);
1149
1150 rrdentry_t *entry;
1151 if ((entry = (rrdentry_t *)g_hash_table_lookup(rrdhash, key))) {
1152 g_free(entry->data);
1153 entry->data = g_memdup(data, len);
1154 entry->len = len;
1155 entry->time = time(NULL);
1156 } else {
1157 rrdentry_t *entry = g_new0(rrdentry_t, 1);
1158
1159 entry->key = g_strdup(key);
1160 entry->data = g_memdup(data, len);
1161 entry->len = len;
1162 entry->time = time(NULL);
1163
1164 g_hash_table_replace(rrdhash, entry->key, entry);
1165 }
1166
1167 update_rrd_data(key, data, len);
1168
1169 return TRUE;
1170}
1171
1172static int
1173kvstore_send_update_message(
1174 dfsm_t *dfsm,
1175 const char *key,
1176 gpointer data,
1177 guint32 len)
1178{
af2e9dd4
DM
1179 if (!dfsm_is_initialized(dfsm))
1180 return -EACCES;
fe000966
DM
1181
1182 struct iovec iov[2];
1183
1184 char name[256];
1185 g_strlcpy(name, key, sizeof(name));
1186
1187 iov[0].iov_base = &name;
1188 iov[0].iov_len = sizeof(name);
1189
1190 iov[1].iov_base = (char *)data;
1191 iov[1].iov_len = len;
1192
1193 if (dfsm_send_message(dfsm, KVSTORE_MESSAGE_UPDATE, iov, 2) == CS_OK)
1194 return 0;
1195
1196 return -EACCES;
1197}
1198
1199static clog_entry_t *
1200kvstore_parse_log_message(
1201 const void *msg,
1202 size_t msg_len)
1203{
1204 g_return_val_if_fail(msg != NULL, NULL);
1205
1206 if (msg_len < sizeof(clog_entry_t)) {
e5a5a3ea 1207 cfs_critical("received short log message (%zu < %zu)", msg_len, sizeof(clog_entry_t));
fe000966
DM
1208 return NULL;
1209 }
1210
1211 clog_entry_t *entry = (clog_entry_t *)msg;
1212
1213 uint32_t size = sizeof(clog_entry_t) + entry->node_len +
1214 entry->ident_len + entry->tag_len + entry->msg_len;
1215
1216 if (msg_len != size) {
e5a5a3ea 1217 cfs_critical("received log message with wrong size (%zu != %u)", msg_len, size);
fe000966
DM
1218 return NULL;
1219 }
1220
1221 msg = entry->data;
1222
1223 if (*((char *)msg + entry->node_len - 1)) {
1224 cfs_critical("unterminated string in log message");
1225 return NULL;
1226 }
1227 msg += entry->node_len;
1228
1229 if (*((char *)msg + entry->ident_len - 1)) {
1230 cfs_critical("unterminated string in log message");
1231 return NULL;
1232 }
1233 msg += entry->ident_len;
1234
1235 if (*((char *)msg + entry->tag_len - 1)) {
1236 cfs_critical("unterminated string in log message");
1237 return NULL;
1238 }
1239 msg += entry->tag_len;
1240
1241 if (*((char *)msg + entry->msg_len - 1)) {
1242 cfs_critical("unterminated string in log message");
1243 return NULL;
1244 }
1245
1246 return entry;
1247}
1248
1249static gboolean
1250kvstore_parse_update_message(
1251 const void *msg,
1252 size_t msg_len,
1253 const char **key,
1254 gconstpointer *data,
1255 guint32 *len)
1256{
1257 g_return_val_if_fail(msg != NULL, FALSE);
1258 g_return_val_if_fail(key != NULL, FALSE);
1259 g_return_val_if_fail(data != NULL, FALSE);
1260 g_return_val_if_fail(len != NULL, FALSE);
1261
1262 if (msg_len < 256) {
e5a5a3ea 1263 cfs_critical("received short kvstore message (%zu < 256)", msg_len);
fe000966
DM
1264 return FALSE;
1265 }
1266
1267 /* test if key is null terminated */
1268 int i = 0;
1269 for (i = 0; i < 256; i++)
1270 if (((char *)msg)[i] == 0)
1271 break;
1272
1273 if (i == 256)
1274 return FALSE;
1275
1276
1277 *len = msg_len - 256;
1278 *key = msg;
1279 *data = msg + 256;
1280
1281 return TRUE;
1282}
1283
1284int
1285cfs_create_status_msg(
1286 GString *str,
1287 const char *nodename,
1288 const char *key)
1289{
1290 g_return_val_if_fail(str != NULL, -EINVAL);
1291 g_return_val_if_fail(key != NULL, -EINVAL);
1292
1293 int res = -ENOENT;
1294
1295 GHashTable *kvhash = NULL;
1296
89fde9ac 1297 g_mutex_lock (&mutex);
fe000966
DM
1298
1299 if (!nodename || !nodename[0] || !strcmp(nodename, cfs.nodename)) {
1300 kvhash = cfs_status.kvhash;
1301 } else {
1302 cfs_clnode_t *clnode;
1303 if ((clnode = g_hash_table_lookup(cfs_status.clinfo->nodes_byname, nodename)))
1304 kvhash = clnode->kvhash;
1305 }
1306
1307 kventry_t *entry;
1308 if (kvhash && (entry = (kventry_t *)g_hash_table_lookup(kvhash, key))) {
1309 g_string_append_len(str, entry->data, entry->len);
1310 res = 0;
1311 }
1312
89fde9ac 1313 g_mutex_unlock (&mutex);
fe000966
DM
1314
1315 return res;
1316}
1317
1318int
1319cfs_status_set(
1320 const char *key,
1321 gpointer data,
1322 size_t len)
1323{
1324 g_return_val_if_fail(key != NULL, FALSE);
1325 g_return_val_if_fail(data != NULL, FALSE);
1326 g_return_val_if_fail(cfs_status.kvhash != NULL, FALSE);
1327
1328 if (len > CFS_MAX_STATUS_SIZE)
1329 return -EFBIG;
1330
89fde9ac 1331 g_mutex_lock (&mutex);
fe000966
DM
1332
1333 gboolean res;
1334
1335 if (strncmp(key, "rrd/", 4) == 0) {
1336 res = rrdentry_hash_set(cfs_status.rrdhash, key + 4, data, len);
1337 } else if (!strcmp(key, "nodeip")) {
1338 res = nodeip_hash_set(cfs_status.iphash, cfs.nodename, data, len);
1339 } else {
1340 res = kventry_hash_set(cfs_status.kvhash, key, data, len);
1341 }
89fde9ac 1342 g_mutex_unlock (&mutex);
fe000966
DM
1343
1344 if (cfs_status.kvstore)
1345 kvstore_send_update_message(cfs_status.kvstore, key, data, len);
1346
1347 return res ? 0 : -ENOMEM;
1348}
1349
1350gboolean
1351cfs_kvstore_node_set(
1352 uint32_t nodeid,
1353 const char *key,
1354 gconstpointer data,
1355 size_t len)
1356{
1357 g_return_val_if_fail(nodeid != 0, FALSE);
1358 g_return_val_if_fail(key != NULL, FALSE);
1359 g_return_val_if_fail(data != NULL, FALSE);
1360
89fde9ac 1361 g_mutex_lock (&mutex);
fe000966
DM
1362
1363 if (!cfs_status.clinfo || !cfs_status.clinfo->nodes_byid)
1364 goto ret; /* ignore */
1365
1366 cfs_clnode_t *clnode = g_hash_table_lookup(cfs_status.clinfo->nodes_byid, &nodeid);
1367 if (!clnode)
1368 goto ret; /* ignore */
1369
1370 cfs_debug("got node %d status update %s", nodeid, key);
1371
1372 if (strncmp(key, "rrd/", 4) == 0) {
1373 rrdentry_hash_set(cfs_status.rrdhash, key + 4, data, len);
1374 } else if (!strcmp(key, "nodeip")) {
1375 nodeip_hash_set(cfs_status.iphash, clnode->name, data, len);
1376 } else {
1377 if (!clnode->kvhash) {
1378 if (!(clnode->kvhash = kventry_hash_new())) {
1379 goto ret; /*ignore */
1380 }
1381 }
1382
1383 kventry_hash_set(clnode->kvhash, key, data, len);
1384
1385 }
1386ret:
89fde9ac 1387 g_mutex_unlock (&mutex);
fe000966
DM
1388
1389 return TRUE;
1390}
1391
1392static gboolean
1393cfs_kvstore_sync(void)
1394{
1395 g_return_val_if_fail(cfs_status.kvhash != NULL, FALSE);
1396 g_return_val_if_fail(cfs_status.kvstore != NULL, FALSE);
1397
1398 gboolean res = TRUE;
1399
89fde9ac 1400 g_mutex_lock (&mutex);
fe000966
DM
1401
1402 GHashTable *ht = cfs_status.kvhash;
1403 GHashTableIter iter;
1404 gpointer key, value;
1405
1406 g_hash_table_iter_init (&iter, ht);
1407
1408 while (g_hash_table_iter_next (&iter, &key, &value)) {
1409 kventry_t *entry = (kventry_t *)value;
1410 kvstore_send_update_message(cfs_status.kvstore, entry->key, entry->data, entry->len);
1411 }
1412
89fde9ac 1413 g_mutex_unlock (&mutex);
fe000966
DM
1414
1415 return res;
1416}
1417
1418static int
1419dfsm_deliver(
1420 dfsm_t *dfsm,
1421 gpointer data,
1422 int *res_ptr,
1423 uint32_t nodeid,
1424 uint32_t pid,
1425 uint16_t msg_type,
1426 uint32_t msg_time,
1427 const void *msg,
1428 size_t msg_len)
1429{
1430 g_return_val_if_fail(dfsm != NULL, -1);
1431 g_return_val_if_fail(msg != NULL, -1);
1432 g_return_val_if_fail(res_ptr != NULL, -1);
1433
1434 /* ignore message for ourself */
1435 if (dfsm_nodeid_is_local(dfsm, nodeid, pid))
1436 goto ret;
1437
1438 if (msg_type == KVSTORE_MESSAGE_UPDATE) {
1439 const char *key;
1440 gconstpointer data;
1441 guint32 len;
1442 if (kvstore_parse_update_message(msg, msg_len, &key, &data, &len)) {
1443 cfs_kvstore_node_set(nodeid, key, data, len);
1444 } else {
1445 cfs_critical("cant parse update message");
1446 }
1447 } else if (msg_type == KVSTORE_MESSAGE_LOG) {
1448 cfs_message("received log"); // fixme: remove
1449 const clog_entry_t *entry;
1450 if ((entry = kvstore_parse_log_message(msg, msg_len))) {
1451 clusterlog_insert(cfs_status.clusterlog, entry);
1452 } else {
1453 cfs_critical("cant parse log message");
1454 }
1455 } else {
1456 cfs_critical("received unknown message type %d\n", msg_type);
1457 goto fail;
1458 }
1459
1460ret:
1461 *res_ptr = 0;
1462 return 1;
1463
1464fail:
1465 *res_ptr = -EACCES;
1466 return 1;
1467}
1468
1469static void
1470dfsm_confchg(
1471 dfsm_t *dfsm,
1472 gpointer data,
1473 const struct cpg_address *member_list,
1474 size_t member_list_entries)
1475{
1476 g_return_if_fail(dfsm != NULL);
1477 g_return_if_fail(member_list != NULL);
1478
1479 cfs_debug("enter %s", __func__);
1480
89fde9ac 1481 g_mutex_lock (&mutex);
fe000966
DM
1482
1483 cfs_clinfo_t *clinfo = cfs_status.clinfo;
1484
1485 if (clinfo && clinfo->nodes_byid) {
1486
1487 GHashTable *ht = clinfo->nodes_byid;
1488 GHashTableIter iter;
1489 gpointer key, value;
1490
1491 g_hash_table_iter_init (&iter, ht);
1492
1493 while (g_hash_table_iter_next (&iter, &key, &value)) {
1494 cfs_clnode_t *node = (cfs_clnode_t *)value;
1495 node->online = FALSE;
1496 }
1497
1498 for (int i = 0; i < member_list_entries; i++) {
1499 cfs_clnode_t *node;
1500 if ((node = g_hash_table_lookup(clinfo->nodes_byid, &member_list[i].nodeid))) {
1501 node->online = TRUE;
1502 }
1503 }
1504
1505 cfs_status.clinfo_version++;
1506 }
1507
89fde9ac 1508 g_mutex_unlock (&mutex);
fe000966
DM
1509}
1510
1511static gpointer
1512dfsm_get_state(
1513 dfsm_t *dfsm,
1514 gpointer data,
1515 unsigned int *res_len)
1516{
1517 g_return_val_if_fail(dfsm != NULL, NULL);
1518
1519 gpointer msg = clusterlog_get_state(cfs_status.clusterlog, res_len);
1520
1521 return msg;
1522}
1523
1524static int
1525dfsm_process_update(
1526 dfsm_t *dfsm,
1527 gpointer data,
1528 dfsm_sync_info_t *syncinfo,
1529 uint32_t nodeid,
1530 uint32_t pid,
1531 const void *msg,
1532 size_t msg_len)
1533{
1534 cfs_critical("%s: received unexpected update message", __func__);
1535
1536 return -1;
1537}
1538
1539static int
1540dfsm_process_state_update(
1541 dfsm_t *dfsm,
1542 gpointer data,
1543 dfsm_sync_info_t *syncinfo)
1544{
1545 g_return_val_if_fail(dfsm != NULL, -1);
1546 g_return_val_if_fail(syncinfo != NULL, -1);
1547
1548 clog_base_t *clog[syncinfo->node_count];
1549
1550 int local_index = -1;
1551 for (int i = 0; i < syncinfo->node_count; i++) {
1552 dfsm_node_info_t *ni = &syncinfo->nodes[i];
1553 ni->synced = 1;
1554
1555 if (syncinfo->local == ni)
1556 local_index = i;
1557
1558 clog_base_t *base = (clog_base_t *)ni->state;
1559 if (ni->state_len > 8 && ni->state_len == clog_size(base)) {
1560 clog[i] = ni->state;
1561 } else {
1562 cfs_critical("received log with wrong size %u", ni->state_len);
1563 clog[i] = NULL;
1564 }
1565 }
1566
1567 if (!clusterlog_merge(cfs_status.clusterlog, clog, syncinfo->node_count, local_index)) {
1568 cfs_critical("unable to merge log files");
1569 }
1570
1571 cfs_kvstore_sync();
1572
1573 return 1;
1574}
1575
1576static int
1577dfsm_commit(
1578 dfsm_t *dfsm,
1579 gpointer data,
1580 dfsm_sync_info_t *syncinfo)
1581{
1582 g_return_val_if_fail(dfsm != NULL, -1);
1583 g_return_val_if_fail(syncinfo != NULL, -1);
1584
1585 return 1;
1586}
1587
1588static void
1589dfsm_synced(dfsm_t *dfsm)
1590{
1591 g_return_if_fail(dfsm != NULL);
1592
1593 char *ip = (char *)g_hash_table_lookup(cfs_status.iphash, cfs.nodename);
1594 if (!ip)
1595 ip = cfs.ip;
1596
1597 cfs_status_set("nodeip", ip, strlen(ip) + 1);
1598}
1599
1600static int
1601dfsm_cleanup(
1602 dfsm_t *dfsm,
1603 gpointer data,
1604 dfsm_sync_info_t *syncinfo)
1605{
1606 return 1;
1607}
1608
1609static dfsm_callbacks_t kvstore_dfsm_callbacks = {
1610 .dfsm_deliver_fn = dfsm_deliver,
1611 .dfsm_confchg_fn = dfsm_confchg,
1612
1613 .dfsm_get_state_fn = dfsm_get_state,
1614 .dfsm_process_state_update_fn = dfsm_process_state_update,
1615 .dfsm_process_update_fn = dfsm_process_update,
1616 .dfsm_commit_fn = dfsm_commit,
1617 .dfsm_cleanup_fn = dfsm_cleanup,
1618 .dfsm_synced_fn = dfsm_synced,
1619};
1620
1621dfsm_t *
1622cfs_status_dfsm_new(void)
1623{
89fde9ac 1624 g_mutex_lock (&mutex);
fe000966
DM
1625
1626 cfs_status.kvstore = dfsm_new(NULL, KVSTORE_CPG_GROUP_NAME, G_LOG_DOMAIN,
1627 0, &kvstore_dfsm_callbacks);
89fde9ac 1628 g_mutex_unlock (&mutex);
fe000966
DM
1629
1630 return cfs_status.kvstore;
1631}
1632
1633gboolean
1634cfs_is_quorate(void)
1635{
89fde9ac 1636 g_mutex_lock (&mutex);
fe000966 1637 gboolean res = cfs_status.quorate;
89fde9ac 1638 g_mutex_unlock (&mutex);
fe000966
DM
1639
1640 return res;
1641}
1642
1643void
1644cfs_set_quorate(
1645 uint32_t quorate,
1646 gboolean quiet)
1647{
89fde9ac 1648 g_mutex_lock (&mutex);
fe000966
DM
1649
1650 uint32_t prev_quorate = cfs_status.quorate;
1651 cfs_status.quorate = quorate;
1652
1653 if (!prev_quorate && cfs_status.quorate) {
1654 if (!quiet)
1655 cfs_message("node has quorum");
1656 }
1657
1658 if (prev_quorate && !cfs_status.quorate) {
1659 if (!quiet)
1660 cfs_message("node lost quorum");
1661 }
1662
89fde9ac 1663 g_mutex_unlock (&mutex);
fe000966
DM
1664}
1665