2 Copyright (C) 2010 - 2020 Proxmox Server Solutions GmbH
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.
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.
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/>.
17 Author: Dietmar Maurer <dietmar@proxmox.com>
21 #define G_LOG_DOMAIN "status"
25 #endif /* HAVE_CONFIG_H */
32 #include <sys/syslog.h>
34 #include <rrd_client.h>
38 #include "cfs-utils.h"
43 #define KVSTORE_CPG_GROUP_NAME "pve_kvstore_v1"
46 KVSTORE_MESSAGE_UPDATE
= 1,
47 KVSTORE_MESSAGE_UPDATE_COMPLETE
= 2,
48 KVSTORE_MESSAGE_LOG
= 3,
51 static uint32_t vminfo_version_counter
;
79 static memdb_change_t memdb_change_array
[] = {
80 { .path
= "corosync.conf" },
81 { .path
= "corosync.conf.new" },
82 { .path
= "storage.cfg" },
83 { .path
= "user.cfg" },
84 { .path
= "domains.cfg" },
85 { .path
= "notifications.cfg"},
86 { .path
= "priv/notifications.cfg"},
87 { .path
= "priv/shadow.cfg" },
88 { .path
= "priv/acme/plugins.cfg" },
89 { .path
= "priv/tfa.cfg" },
90 { .path
= "priv/token.cfg" },
91 { .path
= "priv/ipam.db" },
92 { .path
= "priv/macs.db" },
93 { .path
= "datacenter.cfg" },
94 { .path
= "vzdump.cron" },
95 { .path
= "vzdump.conf" },
96 { .path
= "jobs.cfg" },
97 { .path
= "ha/crm_commands" },
98 { .path
= "ha/manager_status" },
99 { .path
= "ha/resources.cfg" },
100 { .path
= "ha/groups.cfg" },
101 { .path
= "ha/fence.cfg" },
102 { .path
= "status.cfg" },
103 { .path
= "replication.cfg" },
104 { .path
= "ceph.conf" },
105 { .path
= "sdn/vnets.cfg" },
106 { .path
= "sdn/zones.cfg" },
107 { .path
= "sdn/controllers.cfg" },
108 { .path
= "sdn/subnets.cfg" },
109 { .path
= "sdn/ipams.cfg" },
110 { .path
= "sdn/dns.cfg" },
111 { .path
= "sdn/.running-config" },
112 { .path
= "virtual-guest/cpu-models.conf" },
113 { .path
= "virtual-guest/profiles.cfg" },
114 { .path
= "firewall/cluster.fw" },
115 { .path
= "mapping/pci.cfg" },
116 { .path
= "mapping/usb.cfg" },
126 cfs_clinfo_t
*clinfo
;
127 uint32_t clinfo_version
;
130 uint32_t vmlist_version
;
137 GHashTable
*memdb_changes
;
139 clusterlog_t
*clusterlog
;
142 static cfs_status_t cfs_status
;
154 uint32_t cman_version
;
156 GHashTable
*nodes_byid
;
157 GHashTable
*nodes_byname
;
161 g_int32_hash (gconstpointer v
)
163 return *(const uint32_t *) v
;
167 g_int32_equal (gconstpointer v1
,
170 return *((const uint32_t*) v1
) == *((const uint32_t*) v2
);
173 static void vminfo_free(vminfo_t
*vminfo
)
175 g_return_if_fail(vminfo
!= NULL
);
177 if (vminfo
->nodename
)
178 g_free(vminfo
->nodename
);
184 static const char *vminfo_type_to_string(vminfo_t
*vminfo
)
186 if (vminfo
->vmtype
== VMTYPE_QEMU
) {
188 } else if (vminfo
->vmtype
== VMTYPE_OPENVZ
) {
189 // FIXME: remove openvz stuff for 7.x
191 } else if (vminfo
->vmtype
== VMTYPE_LXC
) {
198 static const char *vminfo_type_to_path_type(vminfo_t
*vminfo
)
200 if (vminfo
->vmtype
== VMTYPE_QEMU
) {
201 return "qemu-server"; // special case..
203 return vminfo_type_to_string(vminfo
);
207 int vminfo_to_path(vminfo_t
*vminfo
, GString
*path
)
209 g_return_val_if_fail(vminfo
!= NULL
, -1);
210 g_return_val_if_fail(path
!= NULL
, -1);
212 if (!vminfo
->nodename
)
215 const char *type
= vminfo_type_to_path_type(vminfo
);
216 g_string_printf(path
, "/nodes/%s/%s/%u.conf", vminfo
->nodename
, type
, vminfo
->vmid
);
221 void cfs_clnode_destroy(
222 cfs_clnode_t
*clnode
)
224 g_return_if_fail(clnode
!= NULL
);
227 g_hash_table_destroy(clnode
->kvhash
);
230 g_free(clnode
->name
);
235 cfs_clnode_t
*cfs_clnode_new(
240 g_return_val_if_fail(name
!= NULL
, NULL
);
242 cfs_clnode_t
*clnode
= g_new0(cfs_clnode_t
, 1);
246 clnode
->name
= g_strdup(name
);
247 clnode
->nodeid
= nodeid
;
248 clnode
->votes
= votes
;
253 gboolean
cfs_clinfo_destroy(
254 cfs_clinfo_t
*clinfo
)
256 g_return_val_if_fail(clinfo
!= NULL
, FALSE
);
258 if (clinfo
->cluster_name
)
259 g_free(clinfo
->cluster_name
);
261 if (clinfo
->nodes_byname
)
262 g_hash_table_destroy(clinfo
->nodes_byname
);
264 if (clinfo
->nodes_byid
)
265 g_hash_table_destroy(clinfo
->nodes_byid
);
272 cfs_clinfo_t
*cfs_clinfo_new(
273 const char *cluster_name
,
274 uint32_t cman_version
)
276 g_return_val_if_fail(cluster_name
!= NULL
, NULL
);
278 cfs_clinfo_t
*clinfo
= g_new0(cfs_clinfo_t
, 1);
282 clinfo
->cluster_name
= g_strdup(cluster_name
);
283 clinfo
->cman_version
= cman_version
;
285 if (!(clinfo
->nodes_byid
= g_hash_table_new_full(
286 g_int32_hash
, g_int32_equal
, NULL
,
287 (GDestroyNotify
)cfs_clnode_destroy
)))
290 if (!(clinfo
->nodes_byname
= g_hash_table_new(g_str_hash
, g_str_equal
)))
296 cfs_clinfo_destroy(clinfo
);
301 gboolean
cfs_clinfo_add_node(
302 cfs_clinfo_t
*clinfo
,
303 cfs_clnode_t
*clnode
)
305 g_return_val_if_fail(clinfo
!= NULL
, FALSE
);
306 g_return_val_if_fail(clnode
!= NULL
, FALSE
);
308 g_hash_table_replace(clinfo
->nodes_byid
, &clnode
->nodeid
, clnode
);
309 g_hash_table_replace(clinfo
->nodes_byname
, clnode
->name
, clnode
);
315 cfs_create_memberlist_msg(
318 g_return_val_if_fail(str
!= NULL
, -EINVAL
);
320 g_mutex_lock (&mutex
);
322 g_string_append_printf(str
,"{\n");
326 cfs_clinfo_t
*clinfo
= cfs_status
.clinfo
;
328 if (clinfo
&& clinfo
->nodes_byid
)
329 nodecount
= g_hash_table_size(clinfo
->nodes_byid
);
332 g_string_append_printf(str
, "\"nodename\": \"%s\",\n", cfs
.nodename
);
333 g_string_append_printf(str
, "\"version\": %u,\n", cfs_status
.clinfo_version
);
335 g_string_append_printf(str
, "\"cluster\": { ");
336 g_string_append_printf(str
, "\"name\": \"%s\", \"version\": %d, "
337 "\"nodes\": %d, \"quorate\": %d ",
338 clinfo
->cluster_name
, clinfo
->cman_version
,
339 nodecount
, cfs_status
.quorate
);
341 g_string_append_printf(str
,"},\n");
342 g_string_append_printf(str
,"\"nodelist\": {\n");
344 GHashTable
*ht
= clinfo
->nodes_byid
;
348 g_hash_table_iter_init (&iter
, ht
);
351 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
352 cfs_clnode_t
*node
= (cfs_clnode_t
*)value
;
353 if (i
) g_string_append_printf(str
, ",\n");
356 g_string_append_printf(str
, " \"%s\": { \"id\": %d, \"online\": %d",
357 node
->name
, node
->nodeid
, node
->online
);
360 char *ip
= (char *)g_hash_table_lookup(cfs_status
.iphash
, node
->name
);
362 g_string_append_printf(str
, ", \"ip\": \"%s\"", ip
);
365 g_string_append_printf(str
, "}");
368 g_string_append_printf(str
,"\n }\n");
370 g_string_append_printf(str
, "\"nodename\": \"%s\",\n", cfs
.nodename
);
371 g_string_append_printf(str
, "\"version\": %u\n", cfs_status
.clinfo_version
);
374 g_string_append_printf(str
,"}\n");
376 g_mutex_unlock (&mutex
);
382 kventry_free(kventry_t
*entry
)
384 g_return_if_fail(entry
!= NULL
);
392 kventry_hash_new(void)
394 return g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
,
395 (GDestroyNotify
)kventry_free
);
399 rrdentry_free(rrdentry_t
*entry
)
401 g_return_if_fail(entry
!= NULL
);
409 rrdentry_hash_new(void)
411 return g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
,
412 (GDestroyNotify
)rrdentry_free
);
416 cfs_cluster_log_dump(GString
*str
, const char *user
, guint max_entries
)
418 clusterlog_dump(cfs_status
.clusterlog
, str
, user
, max_entries
);
422 cfs_cluster_log(clog_entry_t
*entry
)
424 g_return_if_fail(entry
!= NULL
);
426 clusterlog_insert(cfs_status
.clusterlog
, entry
);
428 if (cfs_status
.kvstore
) {
430 iov
[0].iov_base
= (char *)entry
;
431 iov
[0].iov_len
= clog_entry_size(entry
);
433 if (dfsm_is_initialized(cfs_status
.kvstore
))
434 dfsm_send_message(cfs_status
.kvstore
, KVSTORE_MESSAGE_LOG
, iov
, 1);
438 void cfs_status_init(void)
440 g_mutex_lock (&mutex
);
442 cfs_status
.start_time
= time(NULL
);
444 cfs_status
.vmlist
= vmlist_hash_new();
446 cfs_status
.kvhash
= kventry_hash_new();
448 cfs_status
.rrdhash
= rrdentry_hash_new();
450 cfs_status
.iphash
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
452 cfs_status
.memdb_changes
= g_hash_table_new(g_str_hash
, g_str_equal
);
454 for (int i
= 0; i
< G_N_ELEMENTS(memdb_change_array
); i
++) {
455 g_hash_table_replace(cfs_status
.memdb_changes
,
456 memdb_change_array
[i
].path
,
457 &memdb_change_array
[i
]);
460 cfs_status
.clusterlog
= clusterlog_new();
463 clusterlog_add(cfs_status
.clusterlog
, "root", "cluster", getpid(),
464 LOG_INFO
, "starting cluster log");
466 g_mutex_unlock (&mutex
);
469 void cfs_status_cleanup(void)
471 g_mutex_lock (&mutex
);
473 cfs_status
.clinfo_version
++;
475 if (cfs_status
.clinfo
) {
476 cfs_clinfo_destroy(cfs_status
.clinfo
);
477 cfs_status
.clinfo
= NULL
;
480 if (cfs_status
.vmlist
) {
481 g_hash_table_destroy(cfs_status
.vmlist
);
482 cfs_status
.vmlist
= NULL
;
485 if (cfs_status
.kvhash
) {
486 g_hash_table_destroy(cfs_status
.kvhash
);
487 cfs_status
.kvhash
= NULL
;
490 if (cfs_status
.rrdhash
) {
491 g_hash_table_destroy(cfs_status
.rrdhash
);
492 cfs_status
.rrdhash
= NULL
;
495 if (cfs_status
.iphash
) {
496 g_hash_table_destroy(cfs_status
.iphash
);
497 cfs_status
.iphash
= NULL
;
500 if (cfs_status
.clusterlog
)
501 clusterlog_destroy(cfs_status
.clusterlog
);
503 g_mutex_unlock (&mutex
);
506 void cfs_status_set_clinfo(
507 cfs_clinfo_t
*clinfo
)
509 g_return_if_fail(clinfo
!= NULL
);
511 g_mutex_lock (&mutex
);
513 cfs_status
.clinfo_version
++;
515 cfs_clinfo_t
*old
= cfs_status
.clinfo
;
517 cfs_status
.clinfo
= clinfo
;
519 cfs_message("update cluster info (cluster name %s, version = %d)",
520 clinfo
->cluster_name
, clinfo
->cman_version
);
523 if (old
&& old
->nodes_byid
&& clinfo
->nodes_byid
) {
525 GHashTable
*ht
= clinfo
->nodes_byid
;
529 g_hash_table_iter_init (&iter
, ht
);
531 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
532 cfs_clnode_t
*node
= (cfs_clnode_t
*)value
;
533 cfs_clnode_t
*oldnode
;
534 if ((oldnode
= g_hash_table_lookup(old
->nodes_byid
, key
))) {
535 node
->online
= oldnode
->online
;
536 node
->kvhash
= oldnode
->kvhash
;
537 oldnode
->kvhash
= NULL
;
544 cfs_clinfo_destroy(old
);
547 g_mutex_unlock (&mutex
);
551 dump_kvstore_versions(
554 const char *nodename
)
556 g_return_if_fail(kvhash
!= NULL
);
557 g_return_if_fail(str
!= NULL
);
558 g_return_if_fail(nodename
!= NULL
);
560 GHashTable
*ht
= kvhash
;
564 g_string_append_printf(str
, "\"%s\": {\n", nodename
);
566 g_hash_table_iter_init (&iter
, ht
);
569 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
570 kventry_t
*entry
= (kventry_t
*)value
;
571 if (i
) g_string_append_printf(str
, ",\n");
573 g_string_append_printf(str
,"\"%s\": %u", entry
->key
, entry
->version
);
576 g_string_append_printf(str
, "}\n");
580 cfs_create_version_msg(GString
*str
)
582 g_return_val_if_fail(str
!= NULL
, -EINVAL
);
584 g_mutex_lock (&mutex
);
586 g_string_append_printf(str
,"{\n");
588 g_string_append_printf(str
, "\"starttime\": %lu,\n", (unsigned long)cfs_status
.start_time
);
590 g_string_append_printf(str
, "\"clinfo\": %u,\n", cfs_status
.clinfo_version
);
592 g_string_append_printf(str
, "\"vmlist\": %u,\n", cfs_status
.vmlist_version
);
594 for (int i
= 0; i
< G_N_ELEMENTS(memdb_change_array
); i
++) {
595 g_string_append_printf(str
, "\"%s\": %u,\n",
596 memdb_change_array
[i
].path
,
597 memdb_change_array
[i
].version
);
600 g_string_append_printf(str
, "\"kvstore\": {\n");
602 dump_kvstore_versions(str
, cfs_status
.kvhash
, cfs
.nodename
);
604 cfs_clinfo_t
*clinfo
= cfs_status
.clinfo
;
606 if (clinfo
&& clinfo
->nodes_byid
) {
607 GHashTable
*ht
= clinfo
->nodes_byid
;
611 g_hash_table_iter_init (&iter
, ht
);
613 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
614 cfs_clnode_t
*node
= (cfs_clnode_t
*)value
;
617 g_string_append_printf(str
, ",\n");
618 dump_kvstore_versions(str
, node
->kvhash
, node
->name
);
622 g_string_append_printf(str
,"}\n");
624 g_string_append_printf(str
,"}\n");
626 g_mutex_unlock (&mutex
);
632 vmlist_hash_new(void)
634 return g_hash_table_new_full(g_int_hash
, g_int_equal
, NULL
,
635 (GDestroyNotify
)vminfo_free
);
639 vmlist_hash_insert_vm(
643 const char *nodename
,
646 g_return_val_if_fail(vmlist
!= NULL
, FALSE
);
647 g_return_val_if_fail(nodename
!= NULL
, FALSE
);
648 g_return_val_if_fail(vmid
!= 0, FALSE
);
649 // FIXME: remove openvz stuff for 7.x
650 g_return_val_if_fail(vmtype
== VMTYPE_QEMU
|| vmtype
== VMTYPE_OPENVZ
||
651 vmtype
== VMTYPE_LXC
, FALSE
);
653 if (!replace
&& g_hash_table_lookup(vmlist
, &vmid
)) {
654 cfs_critical("detected duplicate VMID %d", vmid
);
658 vminfo_t
*vminfo
= g_new0(vminfo_t
, 1);
661 vminfo
->vmtype
= vmtype
;
662 vminfo
->nodename
= g_strdup(nodename
);
664 vminfo
->version
= ++vminfo_version_counter
;
666 g_hash_table_replace(vmlist
, &vminfo
->vmid
, vminfo
);
675 const char *nodename
)
677 g_return_if_fail(cfs_status
.vmlist
!= NULL
);
678 g_return_if_fail(nodename
!= NULL
);
679 g_return_if_fail(vmid
!= 0);
680 // FIXME: remove openvz stuff for 7.x
681 g_return_if_fail(vmtype
== VMTYPE_QEMU
|| vmtype
== VMTYPE_OPENVZ
||
682 vmtype
== VMTYPE_LXC
);
684 cfs_debug("vmlist_register_vm: %s/%u %d", nodename
, vmid
, vmtype
);
686 g_mutex_lock (&mutex
);
688 cfs_status
.vmlist_version
++;
690 vmlist_hash_insert_vm(cfs_status
.vmlist
, vmtype
, vmid
, nodename
, TRUE
);
692 g_mutex_unlock (&mutex
);
696 vmlist_different_vm_exists(
699 const char *nodename
)
701 g_return_val_if_fail(cfs_status
.vmlist
!= NULL
, FALSE
);
702 g_return_val_if_fail(vmid
!= 0, FALSE
);
704 gboolean res
= FALSE
;
706 g_mutex_lock (&mutex
);
709 if ((vminfo
= (vminfo_t
*)g_hash_table_lookup(cfs_status
.vmlist
, &vmid
))) {
710 if (!(vminfo
->vmtype
== vmtype
&& strcmp(vminfo
->nodename
, nodename
) == 0))
713 g_mutex_unlock (&mutex
);
722 g_return_val_if_fail(cfs_status
.vmlist
!= NULL
, FALSE
);
723 g_return_val_if_fail(vmid
!= 0, FALSE
);
725 g_mutex_lock (&mutex
);
727 gpointer res
= g_hash_table_lookup(cfs_status
.vmlist
, &vmid
);
729 g_mutex_unlock (&mutex
);
738 g_return_if_fail(cfs_status
.vmlist
!= NULL
);
739 g_return_if_fail(vmid
!= 0);
741 g_mutex_lock (&mutex
);
743 cfs_status
.vmlist_version
++;
745 g_hash_table_remove(cfs_status
.vmlist
, &vmid
);
747 g_mutex_unlock (&mutex
);
750 void cfs_status_set_vmlist(
753 g_return_if_fail(vmlist
!= NULL
);
755 g_mutex_lock (&mutex
);
757 cfs_status
.vmlist_version
++;
759 if (cfs_status
.vmlist
)
760 g_hash_table_destroy(cfs_status
.vmlist
);
762 cfs_status
.vmlist
= vmlist
;
764 g_mutex_unlock (&mutex
);
768 cfs_create_vmlist_msg(GString
*str
)
770 g_return_val_if_fail(cfs_status
.vmlist
!= NULL
, -EINVAL
);
771 g_return_val_if_fail(str
!= NULL
, -EINVAL
);
773 g_mutex_lock (&mutex
);
775 g_string_append_printf(str
,"{\n");
777 GHashTable
*ht
= cfs_status
.vmlist
;
779 guint count
= g_hash_table_size(ht
);
782 g_string_append_printf(str
,"\"version\": %u\n", cfs_status
.vmlist_version
);
784 g_string_append_printf(str
,"\"version\": %u,\n", cfs_status
.vmlist_version
);
786 g_string_append_printf(str
,"\"ids\": {\n");
791 g_hash_table_iter_init (&iter
, ht
);
794 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
795 vminfo_t
*vminfo
= (vminfo_t
*)value
;
796 const char *type
= vminfo_type_to_string(vminfo
);
799 g_string_append_printf(str
, ",\n");
802 g_string_append_printf(str
,"\"%u\": { \"node\": \"%s\", \"type\": \"%s\", \"version\": %u }",
803 vminfo
->vmid
, vminfo
->nodename
, type
, vminfo
->version
);
806 g_string_append_printf(str
,"}\n");
808 g_string_append_printf(str
,"\n}\n");
810 g_mutex_unlock (&mutex
);
815 // checks if a config line starts with the given prop. if yes, writes a '\0'
816 // at the end of the value, and returns the pointer where the value starts
817 // note: line[line_end] needs to be guaranteed a null byte
819 _get_property_value_from_line(char *line
, size_t line_len
, const char *prop
, size_t prop_len
)
821 if (line_len
<= prop_len
+ 1) return NULL
;
823 if (line
[prop_len
] == ':' && memcmp(line
, prop
, prop_len
) == 0) { // found
824 char *v_start
= &line
[prop_len
+ 1];
825 char *v_end
= &line
[line_len
- 1];
827 // drop initial value whitespaces here already
828 while (v_start
< v_end
&& *v_start
&& isspace(*v_start
)) v_start
++;
830 if (!*v_start
) return NULL
;
832 while (v_end
> v_start
&& isspace(*v_end
)) v_end
--;
833 if (v_end
< &line
[line_len
- 1]) {
843 // checks the conf for lines starting with the given props and
844 // writes the pointers into the correct positions into the 'found' array
845 // afterwards, without initial whitespace(s), we only deal with the format
846 // restriction imposed by our perl VM config parser, main reference is
847 // PVE::QemuServer::parse_vm_config this allows to be very fast and still
849 // main restrictions used for our advantage is the properties match regex:
850 // ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) from parse_vm_config
851 // currently we only look at the current configuration in place, i.e., *no*
852 // snapshot and *no* pending changes
854 // min..max is the char range of the first character of the given props,
855 // so that we can return early when checking the line
856 // note: conf must end with a newline
858 _get_property_values(char **found
, char *conf
, int conf_size
, const char **props
, uint8_t num_props
, char min
, char max
)
860 const char *const conf_end
= conf
+ conf_size
;
862 size_t remaining_size
= conf_size
;
865 if (conf_size
== 0) {
869 char *next_newline
= memchr(conf
, '\n', conf_size
);
870 if (next_newline
== NULL
) {
871 return; // valid property lines end with \n, but none in the config
873 *next_newline
= '\0';
875 while (line
!= NULL
) {
876 if (!line
[0]) goto next
;
878 // snapshot or pending section start, but nothing found yet -> not found
879 if (line
[0] == '[') return;
880 // continue early if line does not begin with the min/max char of the properties
881 if (line
[0] < min
|| line
[0] > max
) goto next
;
883 size_t line_len
= next_newline
- line
;
884 for (uint8_t i
= 0; i
< num_props
; i
++) {
885 char * value
= _get_property_value_from_line(line
, line_len
, props
[i
], strlen(props
[i
]));
887 count
+= (found
[i
] != NULL
) & 0x1; // count newly found lines
891 if (count
== num_props
) {
895 line
= next_newline
+ 1;
896 remaining_size
= conf_end
- line
;
897 next_newline
= memchr(line
, '\n', remaining_size
);
898 if (next_newline
== NULL
) {
899 return; // valid property lines end with \n, but none in the config
901 *next_newline
= '\0';
908 _g_str_append_kv_jsonescaped(GString
*str
, const char *k
, const char *v
)
910 g_string_append_printf(str
, "\"%s\": \"", k
);
913 if (*v
== '\\' || *v
== '"') {
914 g_string_append_c(str
, '\\');
916 g_string_append_c(str
, *v
);
919 g_string_append_c(str
, '"');
923 _print_found_properties(
935 _get_property_values(values
, conf
, size
, props
, num_props
, min
, max
);
938 for (uint8_t i
= 0; i
< num_props
; i
++) {
939 if (values
[i
] == NULL
) {
943 g_string_append_c(str
, ',');
946 g_string_append_printf(str
, ",\n");
950 g_string_append_printf(str
, "\"%u\":{", vmid
);
953 _g_str_append_kv_jsonescaped(str
, props
[i
], values
[i
]);
957 g_string_append_c(str
, '}');
964 cfs_create_guest_conf_properties_msg(GString
*str
, memdb_t
*memdb
, const char **props
, uint8_t num_props
, uint32_t vmid
)
966 g_return_val_if_fail(cfs_status
.vmlist
!= NULL
, -EINVAL
);
967 g_return_val_if_fail(str
!= NULL
, -EINVAL
);
969 // Prelock &memdb->mutex in order to enable the usage of memdb_read_nolock
970 // to prevent Deadlocks as in #2553
971 g_mutex_lock (&memdb
->mutex
);
972 g_mutex_lock (&mutex
);
974 g_string_printf(str
, "{\n");
976 GHashTable
*ht
= cfs_status
.vmlist
;
979 GString
*path
= NULL
;
981 char **values
= calloc(num_props
, sizeof(char*));
982 char min
= 'z', max
= 'a';
984 for (uint8_t i
= 0; i
< num_props
; i
++) {
985 if (props
[i
][0] > max
) {
989 if (props
[i
][0] < min
) {
994 if (!g_hash_table_size(ht
)) {
998 if ((path
= g_string_sized_new(256)) == NULL
) {
1004 vminfo_t
*vminfo
= (vminfo_t
*) g_hash_table_lookup(cfs_status
.vmlist
, &vmid
);
1005 if (vminfo
== NULL
) goto enoent
;
1007 if (!vminfo_to_path(vminfo
, path
)) goto err
;
1009 // use memdb_read_nolock because lock is handled here
1010 int size
= memdb_read_nolock(memdb
, path
->str
, &tmp
);
1011 if (tmp
== NULL
) goto err
;
1013 // conf needs to be newline terminated
1014 if (((char *)tmp
)[size
- 1] != '\n') {
1015 gpointer
new = realloc(tmp
, size
+ 1);
1016 if (new == NULL
) goto err
;
1018 ((char *)tmp
)[size
++] = '\n';
1020 _print_found_properties(str
, tmp
, size
, props
, num_props
, vmid
, values
, min
, max
, 1);
1022 GHashTableIter iter
;
1023 g_hash_table_iter_init (&iter
, ht
);
1025 gpointer key
, value
;
1027 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
1028 vminfo_t
*vminfo
= (vminfo_t
*)value
;
1030 if (!vminfo_to_path(vminfo
, path
)) goto err
;
1032 g_free(tmp
); // no-op if already null
1034 // use memdb_read_nolock because lock is handled here
1035 int size
= memdb_read_nolock(memdb
, path
->str
, &tmp
);
1036 if (tmp
== NULL
) continue;
1038 // conf needs to be newline terminated
1039 if (((char *)tmp
)[size
- 1] != '\n') {
1040 gpointer
new = realloc(tmp
, size
+ 1);
1041 if (new == NULL
) continue;
1043 ((char *)tmp
)[size
++] = '\n';
1046 memset(values
, 0, sizeof(char*)*num_props
); // reset array
1047 first
= _print_found_properties(str
, tmp
, size
, props
, num_props
,
1048 vminfo
->vmid
, values
, min
, max
, first
);
1055 g_string_free(path
, TRUE
);
1057 g_string_append_printf(str
,"\n}\n");
1058 g_mutex_unlock (&mutex
);
1059 g_mutex_unlock (&memdb
->mutex
);
1070 cfs_create_guest_conf_property_msg(GString
*str
, memdb_t
*memdb
, const char *prop
, uint32_t vmid
)
1072 return cfs_create_guest_conf_properties_msg(str
, memdb
, &prop
, 1, vmid
);
1076 record_memdb_change(const char *path
)
1078 g_return_if_fail(cfs_status
.memdb_changes
!= 0);
1082 if ((ce
= (memdb_change_t
*)g_hash_table_lookup(cfs_status
.memdb_changes
, path
))) {
1088 record_memdb_reload(void)
1090 for (int i
= 0; i
< G_N_ELEMENTS(memdb_change_array
); i
++) {
1091 memdb_change_array
[i
].version
++;
1102 g_return_val_if_fail(kvhash
!= NULL
, FALSE
);
1103 g_return_val_if_fail(key
!= NULL
, FALSE
);
1104 g_return_val_if_fail(data
!= NULL
, FALSE
);
1108 g_hash_table_remove(kvhash
, key
);
1109 } else if ((entry
= (kventry_t
*)g_hash_table_lookup(kvhash
, key
))) {
1110 g_free(entry
->data
);
1111 entry
->data
= g_memdup2(data
, len
);
1115 kventry_t
*entry
= g_new0(kventry_t
, 1);
1117 entry
->key
= g_strdup(key
);
1118 entry
->data
= g_memdup2(data
, len
);
1121 g_hash_table_replace(kvhash
, entry
->key
, entry
);
1127 static const char *rrd_def_node
[] = {
1128 "DS:loadavg:GAUGE:120:0:U",
1129 "DS:maxcpu:GAUGE:120:0:U",
1130 "DS:cpu:GAUGE:120:0:U",
1131 "DS:iowait:GAUGE:120:0:U",
1132 "DS:memtotal:GAUGE:120:0:U",
1133 "DS:memused:GAUGE:120:0:U",
1134 "DS:swaptotal:GAUGE:120:0:U",
1135 "DS:swapused:GAUGE:120:0:U",
1136 "DS:roottotal:GAUGE:120:0:U",
1137 "DS:rootused:GAUGE:120:0:U",
1138 "DS:netin:DERIVE:120:0:U",
1139 "DS:netout:DERIVE:120:0:U",
1141 "RRA:AVERAGE:0.5:1:70", // 1 min avg - one hour
1142 "RRA:AVERAGE:0.5:30:70", // 30 min avg - one day
1143 "RRA:AVERAGE:0.5:180:70", // 3 hour avg - one week
1144 "RRA:AVERAGE:0.5:720:70", // 12 hour avg - one month
1145 "RRA:AVERAGE:0.5:10080:70", // 7 day avg - ony year
1147 "RRA:MAX:0.5:1:70", // 1 min max - one hour
1148 "RRA:MAX:0.5:30:70", // 30 min max - one day
1149 "RRA:MAX:0.5:180:70", // 3 hour max - one week
1150 "RRA:MAX:0.5:720:70", // 12 hour max - one month
1151 "RRA:MAX:0.5:10080:70", // 7 day max - ony year
1155 static const char *rrd_def_vm
[] = {
1156 "DS:maxcpu:GAUGE:120:0:U",
1157 "DS:cpu:GAUGE:120:0:U",
1158 "DS:maxmem:GAUGE:120:0:U",
1159 "DS:mem:GAUGE:120:0:U",
1160 "DS:maxdisk:GAUGE:120:0:U",
1161 "DS:disk:GAUGE:120:0:U",
1162 "DS:netin:DERIVE:120:0:U",
1163 "DS:netout:DERIVE:120:0:U",
1164 "DS:diskread:DERIVE:120:0:U",
1165 "DS:diskwrite:DERIVE:120:0:U",
1167 "RRA:AVERAGE:0.5:1:70", // 1 min avg - one hour
1168 "RRA:AVERAGE:0.5:30:70", // 30 min avg - one day
1169 "RRA:AVERAGE:0.5:180:70", // 3 hour avg - one week
1170 "RRA:AVERAGE:0.5:720:70", // 12 hour avg - one month
1171 "RRA:AVERAGE:0.5:10080:70", // 7 day avg - ony year
1173 "RRA:MAX:0.5:1:70", // 1 min max - one hour
1174 "RRA:MAX:0.5:30:70", // 30 min max - one day
1175 "RRA:MAX:0.5:180:70", // 3 hour max - one week
1176 "RRA:MAX:0.5:720:70", // 12 hour max - one month
1177 "RRA:MAX:0.5:10080:70", // 7 day max - ony year
1181 static const char *rrd_def_storage
[] = {
1182 "DS:total:GAUGE:120:0:U",
1183 "DS:used:GAUGE:120:0:U",
1185 "RRA:AVERAGE:0.5:1:70", // 1 min avg - one hour
1186 "RRA:AVERAGE:0.5:30:70", // 30 min avg - one day
1187 "RRA:AVERAGE:0.5:180:70", // 3 hour avg - one week
1188 "RRA:AVERAGE:0.5:720:70", // 12 hour avg - one month
1189 "RRA:AVERAGE:0.5:10080:70", // 7 day avg - ony year
1191 "RRA:MAX:0.5:1:70", // 1 min max - one hour
1192 "RRA:MAX:0.5:30:70", // 30 min max - one day
1193 "RRA:MAX:0.5:180:70", // 3 hour max - one week
1194 "RRA:MAX:0.5:720:70", // 12 hour max - one month
1195 "RRA:MAX:0.5:10080:70", // 7 day max - ony year
1199 #define RRDDIR "/var/lib/rrdcached/db"
1203 const char *filename
,
1205 const char *rrddef
[])
1207 /* start at day boundary */
1210 struct tm
*ltm
= localtime(&ctime
);
1216 if (rrd_create_r(filename
, 60, timelocal(ltm
), argcount
, rrddef
)) {
1217 cfs_message("RRD create error %s: %s", filename
, rrd_get_error());
1221 static inline const char *
1227 while (*data
&& found
< count
) {
1240 g_return_if_fail(key
!= NULL
);
1241 g_return_if_fail(data
!= NULL
);
1242 g_return_if_fail(len
> 0);
1243 g_return_if_fail(len
< 4096);
1245 static const char *rrdcsock
= "unix:/var/run/rrdcached.sock";
1248 if (rrdc_connect(rrdcsock
) != 0)
1251 char *filename
= NULL
;
1255 if (strncmp(key
, "pve2-node/", 10) == 0) {
1256 const char *node
= key
+ 10;
1260 if (strchr(node
, '/') != NULL
)
1263 if (strlen(node
) < 1)
1266 filename
= g_strdup_printf(RRDDIR
"/%s", key
);
1268 if (!g_file_test(filename
, G_FILE_TEST_EXISTS
)) {
1270 mkdir(RRDDIR
"/pve2-node", 0755);
1271 int argcount
= sizeof(rrd_def_node
)/sizeof(void*) - 1;
1272 create_rrd_file(filename
, argcount
, rrd_def_node
);
1275 } else if ((strncmp(key
, "pve2-vm/", 8) == 0) ||
1276 (strncmp(key
, "pve2.3-vm/", 10) == 0)) {
1279 if (strncmp(key
, "pve2-vm/", 8) == 0) {
1287 if (strchr(vmid
, '/') != NULL
)
1290 if (strlen(vmid
) < 1)
1293 filename
= g_strdup_printf(RRDDIR
"/%s/%s", "pve2-vm", vmid
);
1295 if (!g_file_test(filename
, G_FILE_TEST_EXISTS
)) {
1297 mkdir(RRDDIR
"/pve2-vm", 0755);
1298 int argcount
= sizeof(rrd_def_vm
)/sizeof(void*) - 1;
1299 create_rrd_file(filename
, argcount
, rrd_def_vm
);
1302 } else if (strncmp(key
, "pve2-storage/", 13) == 0) {
1303 const char *node
= key
+ 13;
1305 const char *storage
= node
;
1306 while (*storage
&& *storage
!= '/')
1309 if (*storage
!= '/' || ((storage
- node
) < 1))
1314 if (strchr(storage
, '/') != NULL
)
1317 if (strlen(storage
) < 1)
1320 filename
= g_strdup_printf(RRDDIR
"/%s", key
);
1322 if (!g_file_test(filename
, G_FILE_TEST_EXISTS
)) {
1324 mkdir(RRDDIR
"/pve2-storage", 0755);
1326 char *dir
= g_path_get_dirname(filename
);
1330 int argcount
= sizeof(rrd_def_storage
)/sizeof(void*) - 1;
1331 create_rrd_file(filename
, argcount
, rrd_def_storage
);
1338 const char *dp
= skip
? rrd_skip_data(data
, skip
) : data
;
1340 const char *update_args
[] = { dp
, NULL
};
1344 if ((status
= rrdc_update(filename
, 1, update_args
)) != 0) {
1345 cfs_message("RRDC update error %s: %d", filename
, status
);
1348 if (rrd_update_r(filename
, NULL
, 1, update_args
) != 0) {
1349 cfs_message("RRD update error %s: %s", filename
, rrd_get_error());
1355 if (rrd_update_r(filename
, NULL
, 1, update_args
) != 0) {
1356 cfs_message("RRD update error %s: %s", filename
, rrd_get_error());
1367 cfs_critical("RRD update error: unknown/wrong key %s", key
);
1377 rrdentry_t
*entry
= (rrdentry_t
*)value
;
1378 uint32_t ctime
= GPOINTER_TO_UINT(user_data
);
1380 int diff
= ctime
- entry
->time
;
1382 /* remove everything older than 5 minutes */
1385 return (diff
> expire
) ? TRUE
: FALSE
;
1388 static char *rrd_dump_buf
= NULL
;
1389 static time_t rrd_dump_last
= 0;
1392 cfs_rrd_dump(GString
*str
)
1396 g_mutex_lock (&mutex
);
1399 if (rrd_dump_buf
&& (ctime
- rrd_dump_last
) < 2) {
1400 g_string_assign(str
, rrd_dump_buf
);
1401 g_mutex_unlock (&mutex
);
1405 /* remove old data */
1406 g_hash_table_foreach_remove(cfs_status
.rrdhash
, rrd_entry_is_old
,
1407 GUINT_TO_POINTER(ctime
));
1409 g_string_set_size(str
, 0);
1411 GHashTableIter iter
;
1412 gpointer key
, value
;
1414 g_hash_table_iter_init (&iter
, cfs_status
.rrdhash
);
1416 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
1417 rrdentry_t
*entry
= (rrdentry_t
*)value
;
1418 g_string_append(str
, key
);
1419 g_string_append(str
, ":");
1420 g_string_append(str
, entry
->data
);
1421 g_string_append(str
, "\n");
1424 g_string_append_c(str
, 0); // never return undef
1426 rrd_dump_last
= ctime
;
1428 g_free(rrd_dump_buf
);
1429 rrd_dump_buf
= g_strdup(str
->str
);
1431 g_mutex_unlock (&mutex
);
1437 const char *nodename
,
1441 g_return_val_if_fail(iphash
!= NULL
, FALSE
);
1442 g_return_val_if_fail(nodename
!= NULL
, FALSE
);
1443 g_return_val_if_fail(ip
!= NULL
, FALSE
);
1444 g_return_val_if_fail(len
> 0, FALSE
);
1445 g_return_val_if_fail(len
< 256, FALSE
);
1446 g_return_val_if_fail(ip
[len
-1] == 0, FALSE
);
1448 char *oldip
= (char *)g_hash_table_lookup(iphash
, nodename
);
1450 if (!oldip
|| (strcmp(oldip
, ip
) != 0)) {
1451 cfs_status
.clinfo_version
++;
1452 g_hash_table_replace(iphash
, g_strdup(nodename
), g_strdup(ip
));
1460 GHashTable
*rrdhash
,
1465 g_return_val_if_fail(rrdhash
!= NULL
, FALSE
);
1466 g_return_val_if_fail(key
!= NULL
, FALSE
);
1467 g_return_val_if_fail(data
!= NULL
, FALSE
);
1468 g_return_val_if_fail(len
> 0, FALSE
);
1469 g_return_val_if_fail(len
< 4096, FALSE
);
1470 g_return_val_if_fail(data
[len
-1] == 0, FALSE
);
1473 if ((entry
= (rrdentry_t
*)g_hash_table_lookup(rrdhash
, key
))) {
1474 g_free(entry
->data
);
1475 entry
->data
= g_memdup2(data
, len
);
1477 entry
->time
= time(NULL
);
1479 rrdentry_t
*entry
= g_new0(rrdentry_t
, 1);
1481 entry
->key
= g_strdup(key
);
1482 entry
->data
= g_memdup2(data
, len
);
1484 entry
->time
= time(NULL
);
1486 g_hash_table_replace(rrdhash
, entry
->key
, entry
);
1489 update_rrd_data(key
, data
, len
);
1495 kvstore_send_update_message(
1501 if (!dfsm_is_initialized(dfsm
))
1504 struct iovec iov
[2];
1507 g_strlcpy(name
, key
, sizeof(name
));
1509 iov
[0].iov_base
= &name
;
1510 iov
[0].iov_len
= sizeof(name
);
1512 iov
[1].iov_base
= (char *)data
;
1513 iov
[1].iov_len
= len
;
1515 if (dfsm_send_message(dfsm
, KVSTORE_MESSAGE_UPDATE
, iov
, 2) == CS_OK
)
1521 static clog_entry_t
*
1522 kvstore_parse_log_message(
1526 g_return_val_if_fail(msg
!= NULL
, NULL
);
1528 if (msg_len
< sizeof(clog_entry_t
)) {
1529 cfs_critical("received short log message (%zu < %zu)", msg_len
, sizeof(clog_entry_t
));
1533 clog_entry_t
*entry
= (clog_entry_t
*)msg
;
1535 uint32_t size
= sizeof(clog_entry_t
) + entry
->node_len
+
1536 entry
->ident_len
+ entry
->tag_len
+ entry
->msg_len
;
1538 if (msg_len
!= size
) {
1539 cfs_critical("received log message with wrong size (%zu != %u)", msg_len
, size
);
1543 char *msgptr
= entry
->data
;
1545 if (*((char *)msgptr
+ entry
->node_len
- 1)) {
1546 cfs_critical("unterminated string in log message");
1549 msgptr
+= entry
->node_len
;
1551 if (*((char *)msgptr
+ entry
->ident_len
- 1)) {
1552 cfs_critical("unterminated string in log message");
1555 msgptr
+= entry
->ident_len
;
1557 if (*((char *)msgptr
+ entry
->tag_len
- 1)) {
1558 cfs_critical("unterminated string in log message");
1561 msgptr
+= entry
->tag_len
;
1563 if (*((char *)msgptr
+ entry
->msg_len
- 1)) {
1564 cfs_critical("unterminated string in log message");
1572 kvstore_parse_update_message(
1576 gconstpointer
*data
,
1579 g_return_val_if_fail(msg
!= NULL
, FALSE
);
1580 g_return_val_if_fail(key
!= NULL
, FALSE
);
1581 g_return_val_if_fail(data
!= NULL
, FALSE
);
1582 g_return_val_if_fail(len
!= NULL
, FALSE
);
1584 if (msg_len
< 256) {
1585 cfs_critical("received short kvstore message (%zu < 256)", msg_len
);
1589 /* test if key is null terminated */
1591 for (i
= 0; i
< 256; i
++)
1592 if (((char *)msg
)[i
] == 0)
1599 *len
= msg_len
- 256;
1601 *data
= (char *) msg
+ 256;
1607 cfs_create_status_msg(
1609 const char *nodename
,
1612 g_return_val_if_fail(str
!= NULL
, -EINVAL
);
1613 g_return_val_if_fail(key
!= NULL
, -EINVAL
);
1617 GHashTable
*kvhash
= NULL
;
1619 g_mutex_lock (&mutex
);
1621 if (!nodename
|| !nodename
[0] || !strcmp(nodename
, cfs
.nodename
)) {
1622 kvhash
= cfs_status
.kvhash
;
1623 } else if (cfs_status
.clinfo
&& cfs_status
.clinfo
->nodes_byname
) {
1624 cfs_clnode_t
*clnode
;
1625 if ((clnode
= g_hash_table_lookup(cfs_status
.clinfo
->nodes_byname
, nodename
)))
1626 kvhash
= clnode
->kvhash
;
1630 if (kvhash
&& (entry
= (kventry_t
*)g_hash_table_lookup(kvhash
, key
))) {
1631 g_string_append_len(str
, entry
->data
, entry
->len
);
1635 g_mutex_unlock (&mutex
);
1646 g_return_val_if_fail(key
!= NULL
, FALSE
);
1647 g_return_val_if_fail(data
!= NULL
, FALSE
);
1648 g_return_val_if_fail(cfs_status
.kvhash
!= NULL
, FALSE
);
1650 if (len
> CFS_MAX_STATUS_SIZE
)
1653 g_mutex_lock (&mutex
);
1657 if (strncmp(key
, "rrd/", 4) == 0) {
1658 res
= rrdentry_hash_set(cfs_status
.rrdhash
, key
+ 4, data
, len
);
1659 } else if (!strcmp(key
, "nodeip")) {
1660 res
= nodeip_hash_set(cfs_status
.iphash
, cfs
.nodename
, data
, len
);
1662 res
= kventry_hash_set(cfs_status
.kvhash
, key
, data
, len
);
1664 g_mutex_unlock (&mutex
);
1666 if (cfs_status
.kvstore
)
1667 kvstore_send_update_message(cfs_status
.kvstore
, key
, data
, len
);
1669 return res
? 0 : -ENOMEM
;
1673 cfs_kvstore_node_set(
1679 g_return_val_if_fail(nodeid
!= 0, FALSE
);
1680 g_return_val_if_fail(key
!= NULL
, FALSE
);
1681 g_return_val_if_fail(data
!= NULL
, FALSE
);
1683 g_mutex_lock (&mutex
);
1685 if (!cfs_status
.clinfo
|| !cfs_status
.clinfo
->nodes_byid
)
1686 goto ret
; /* ignore */
1688 cfs_clnode_t
*clnode
= g_hash_table_lookup(cfs_status
.clinfo
->nodes_byid
, &nodeid
);
1690 goto ret
; /* ignore */
1692 cfs_debug("got node %d status update %s", nodeid
, key
);
1694 if (strncmp(key
, "rrd/", 4) == 0) {
1695 rrdentry_hash_set(cfs_status
.rrdhash
, key
+ 4, data
, len
);
1696 } else if (!strcmp(key
, "nodeip")) {
1697 nodeip_hash_set(cfs_status
.iphash
, clnode
->name
, data
, len
);
1699 if (!clnode
->kvhash
) {
1700 if (!(clnode
->kvhash
= kventry_hash_new())) {
1701 goto ret
; /*ignore */
1705 kventry_hash_set(clnode
->kvhash
, key
, data
, len
);
1709 g_mutex_unlock (&mutex
);
1715 cfs_kvstore_sync(void)
1717 g_return_val_if_fail(cfs_status
.kvhash
!= NULL
, FALSE
);
1718 g_return_val_if_fail(cfs_status
.kvstore
!= NULL
, FALSE
);
1720 gboolean res
= TRUE
;
1722 g_mutex_lock (&mutex
);
1724 GHashTable
*ht
= cfs_status
.kvhash
;
1725 GHashTableIter iter
;
1726 gpointer key
, value
;
1728 g_hash_table_iter_init (&iter
, ht
);
1730 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
1731 kventry_t
*entry
= (kventry_t
*)value
;
1732 kvstore_send_update_message(cfs_status
.kvstore
, entry
->key
, entry
->data
, entry
->len
);
1735 g_mutex_unlock (&mutex
);
1752 g_return_val_if_fail(dfsm
!= NULL
, -1);
1753 g_return_val_if_fail(msg
!= NULL
, -1);
1754 g_return_val_if_fail(res_ptr
!= NULL
, -1);
1756 /* ignore message for ourself */
1757 if (dfsm_nodeid_is_local(dfsm
, nodeid
, pid
))
1760 if (msg_type
== KVSTORE_MESSAGE_UPDATE
) {
1764 if (kvstore_parse_update_message(msg
, msg_len
, &key
, &data
, &len
)) {
1765 cfs_kvstore_node_set(nodeid
, key
, data
, len
);
1767 cfs_critical("cant parse update message");
1769 } else if (msg_type
== KVSTORE_MESSAGE_LOG
) {
1770 cfs_message("received log"); // fixme: remove
1771 const clog_entry_t
*entry
;
1772 if ((entry
= kvstore_parse_log_message(msg
, msg_len
))) {
1773 clusterlog_insert(cfs_status
.clusterlog
, entry
);
1775 cfs_critical("cant parse log message");
1778 cfs_critical("received unknown message type %d\n", msg_type
);
1795 const struct cpg_address
*member_list
,
1796 size_t member_list_entries
)
1798 g_return_if_fail(dfsm
!= NULL
);
1799 g_return_if_fail(member_list
!= NULL
);
1801 cfs_debug("enter %s", __func__
);
1803 g_mutex_lock (&mutex
);
1805 cfs_clinfo_t
*clinfo
= cfs_status
.clinfo
;
1807 if (clinfo
&& clinfo
->nodes_byid
) {
1809 GHashTable
*ht
= clinfo
->nodes_byid
;
1810 GHashTableIter iter
;
1811 gpointer key
, value
;
1813 g_hash_table_iter_init (&iter
, ht
);
1815 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
1816 cfs_clnode_t
*node
= (cfs_clnode_t
*)value
;
1817 node
->online
= FALSE
;
1820 for (int i
= 0; i
< member_list_entries
; i
++) {
1822 if ((node
= g_hash_table_lookup(clinfo
->nodes_byid
, &member_list
[i
].nodeid
))) {
1823 node
->online
= TRUE
;
1827 cfs_status
.clinfo_version
++;
1830 g_mutex_unlock (&mutex
);
1837 unsigned int *res_len
)
1839 g_return_val_if_fail(dfsm
!= NULL
, NULL
);
1841 gpointer msg
= clusterlog_get_state(cfs_status
.clusterlog
, res_len
);
1847 dfsm_process_update(
1850 dfsm_sync_info_t
*syncinfo
,
1856 cfs_critical("%s: received unexpected update message", __func__
);
1862 dfsm_process_state_update(
1865 dfsm_sync_info_t
*syncinfo
)
1867 g_return_val_if_fail(dfsm
!= NULL
, -1);
1868 g_return_val_if_fail(syncinfo
!= NULL
, -1);
1870 clog_base_t
*clog
[syncinfo
->node_count
];
1872 int local_index
= -1;
1873 for (int i
= 0; i
< syncinfo
->node_count
; i
++) {
1874 dfsm_node_info_t
*ni
= &syncinfo
->nodes
[i
];
1877 if (syncinfo
->local
== ni
)
1880 clog_base_t
*base
= (clog_base_t
*)ni
->state
;
1881 if (ni
->state_len
> 8 && ni
->state_len
== clog_size(base
)) {
1882 clog
[i
] = ni
->state
;
1884 cfs_critical("received log with wrong size %u", ni
->state_len
);
1889 if (!clusterlog_merge(cfs_status
.clusterlog
, clog
, syncinfo
->node_count
, local_index
)) {
1890 cfs_critical("unable to merge log files");
1902 dfsm_sync_info_t
*syncinfo
)
1904 g_return_val_if_fail(dfsm
!= NULL
, -1);
1905 g_return_val_if_fail(syncinfo
!= NULL
, -1);
1911 dfsm_synced(dfsm_t
*dfsm
)
1913 g_return_if_fail(dfsm
!= NULL
);
1915 char *ip
= (char *)g_hash_table_lookup(cfs_status
.iphash
, cfs
.nodename
);
1919 cfs_status_set("nodeip", ip
, strlen(ip
) + 1);
1926 dfsm_sync_info_t
*syncinfo
)
1931 static dfsm_callbacks_t kvstore_dfsm_callbacks
= {
1932 .dfsm_deliver_fn
= dfsm_deliver
,
1933 .dfsm_confchg_fn
= dfsm_confchg
,
1935 .dfsm_get_state_fn
= dfsm_get_state
,
1936 .dfsm_process_state_update_fn
= dfsm_process_state_update
,
1937 .dfsm_process_update_fn
= dfsm_process_update
,
1938 .dfsm_commit_fn
= dfsm_commit
,
1939 .dfsm_cleanup_fn
= dfsm_cleanup
,
1940 .dfsm_synced_fn
= dfsm_synced
,
1944 cfs_status_dfsm_new(void)
1946 g_mutex_lock (&mutex
);
1948 cfs_status
.kvstore
= dfsm_new(NULL
, KVSTORE_CPG_GROUP_NAME
, G_LOG_DOMAIN
,
1949 0, &kvstore_dfsm_callbacks
);
1950 g_mutex_unlock (&mutex
);
1952 return cfs_status
.kvstore
;
1956 cfs_is_quorate(void)
1958 g_mutex_lock (&mutex
);
1959 gboolean res
= cfs_status
.quorate
;
1960 g_mutex_unlock (&mutex
);
1970 g_mutex_lock (&mutex
);
1972 uint32_t prev_quorate
= cfs_status
.quorate
;
1973 cfs_status
.quorate
= quorate
;
1975 if (!prev_quorate
&& cfs_status
.quorate
) {
1977 cfs_message("node has quorum");
1980 if (prev_quorate
&& !cfs_status
.quorate
) {
1982 cfs_message("node lost quorum");
1985 g_mutex_unlock (&mutex
);