2 Copyright (C) 2010 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>
37 #include "cfs-utils.h"
41 #define KVSTORE_CPG_GROUP_NAME "pve_kvstore_v1"
44 KVSTORE_MESSAGE_UPDATE
= 1,
45 KVSTORE_MESSAGE_UPDATE_COMPLETE
= 2,
46 KVSTORE_MESSAGE_LOG
= 3,
49 static uint32_t vminfo_version_counter
;
77 static memdb_change_t memdb_change_array
[] = {
78 { .path
= "corosync.conf" },
79 { .path
= "corosync.conf.new" },
80 { .path
= "storage.cfg" },
81 { .path
= "user.cfg" },
82 { .path
= "domains.cfg" },
83 { .path
= "priv/shadow.cfg" },
84 { .path
= "priv/tfa.cfg" },
85 { .path
= "datacenter.cfg" },
86 { .path
= "vzdump.cron" },
87 { .path
= "ha/crm_commands" },
88 { .path
= "ha/manager_status" },
89 { .path
= "ha/resources.cfg" },
90 { .path
= "ha/groups.cfg" },
91 { .path
= "ha/fence.cfg" },
92 { .path
= "status.cfg" },
93 { .path
= "replication.cfg" },
94 { .path
= "ceph.conf" },
104 cfs_clinfo_t
*clinfo
;
105 uint32_t clinfo_version
;
108 uint32_t vmlist_version
;
115 GHashTable
*memdb_changes
;
117 clusterlog_t
*clusterlog
;
120 static cfs_status_t cfs_status
;
132 uint32_t cman_version
;
134 GHashTable
*nodes_byid
;
135 GHashTable
*nodes_byname
;
139 g_int32_hash (gconstpointer v
)
141 return *(const uint32_t *) v
;
145 g_int32_equal (gconstpointer v1
,
148 return *((const uint32_t*) v1
) == *((const uint32_t*) v2
);
151 static void vminfo_free(vminfo_t
*vminfo
)
153 g_return_if_fail(vminfo
!= NULL
);
155 if (vminfo
->nodename
)
156 g_free(vminfo
->nodename
);
162 static const char *vminfo_type_to_string(vminfo_t
*vminfo
) {
163 if (vminfo
->vmtype
== VMTYPE_QEMU
) {
165 } else if (vminfo
->vmtype
== VMTYPE_OPENVZ
) {
167 } else if (vminfo
->vmtype
== VMTYPE_LXC
) {
174 void cfs_clnode_destroy(
175 cfs_clnode_t
*clnode
)
177 g_return_if_fail(clnode
!= NULL
);
180 g_hash_table_destroy(clnode
->kvhash
);
183 g_free(clnode
->name
);
188 cfs_clnode_t
*cfs_clnode_new(
193 g_return_val_if_fail(name
!= NULL
, NULL
);
195 cfs_clnode_t
*clnode
= g_new0(cfs_clnode_t
, 1);
199 clnode
->name
= g_strdup(name
);
200 clnode
->nodeid
= nodeid
;
201 clnode
->votes
= votes
;
206 gboolean
cfs_clinfo_destroy(
207 cfs_clinfo_t
*clinfo
)
209 g_return_val_if_fail(clinfo
!= NULL
, FALSE
);
211 if (clinfo
->cluster_name
)
212 g_free(clinfo
->cluster_name
);
214 if (clinfo
->nodes_byname
)
215 g_hash_table_destroy(clinfo
->nodes_byname
);
217 if (clinfo
->nodes_byid
)
218 g_hash_table_destroy(clinfo
->nodes_byid
);
225 cfs_clinfo_t
*cfs_clinfo_new(
226 const char *cluster_name
,
227 uint32_t cman_version
)
229 g_return_val_if_fail(cluster_name
!= NULL
, NULL
);
231 cfs_clinfo_t
*clinfo
= g_new0(cfs_clinfo_t
, 1);
235 clinfo
->cluster_name
= g_strdup(cluster_name
);
236 clinfo
->cman_version
= cman_version
;
238 if (!(clinfo
->nodes_byid
= g_hash_table_new_full(
239 g_int32_hash
, g_int32_equal
, NULL
,
240 (GDestroyNotify
)cfs_clnode_destroy
)))
243 if (!(clinfo
->nodes_byname
= g_hash_table_new(g_str_hash
, g_str_equal
)))
249 cfs_clinfo_destroy(clinfo
);
254 gboolean
cfs_clinfo_add_node(
255 cfs_clinfo_t
*clinfo
,
256 cfs_clnode_t
*clnode
)
258 g_return_val_if_fail(clinfo
!= NULL
, FALSE
);
259 g_return_val_if_fail(clnode
!= NULL
, FALSE
);
261 g_hash_table_replace(clinfo
->nodes_byid
, &clnode
->nodeid
, clnode
);
262 g_hash_table_replace(clinfo
->nodes_byname
, clnode
->name
, clnode
);
268 cfs_create_memberlist_msg(
271 g_return_val_if_fail(str
!= NULL
, -EINVAL
);
273 g_mutex_lock (&mutex
);
275 g_string_append_printf(str
,"{\n");
279 cfs_clinfo_t
*clinfo
= cfs_status
.clinfo
;
281 if (clinfo
&& clinfo
->nodes_byid
)
282 nodecount
= g_hash_table_size(clinfo
->nodes_byid
);
285 g_string_append_printf(str
, "\"nodename\": \"%s\",\n", cfs
.nodename
);
286 g_string_append_printf(str
, "\"version\": %u,\n", cfs_status
.clinfo_version
);
288 g_string_append_printf(str
, "\"cluster\": { ");
289 g_string_append_printf(str
, "\"name\": \"%s\", \"version\": %d, "
290 "\"nodes\": %d, \"quorate\": %d ",
291 clinfo
->cluster_name
, clinfo
->cman_version
,
292 nodecount
, cfs_status
.quorate
);
294 g_string_append_printf(str
,"},\n");
295 g_string_append_printf(str
,"\"nodelist\": {\n");
297 GHashTable
*ht
= clinfo
->nodes_byid
;
301 g_hash_table_iter_init (&iter
, ht
);
304 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
305 cfs_clnode_t
*node
= (cfs_clnode_t
*)value
;
306 if (i
) g_string_append_printf(str
, ",\n");
309 g_string_append_printf(str
, " \"%s\": { \"id\": %d, \"online\": %d",
310 node
->name
, node
->nodeid
, node
->online
);
313 char *ip
= (char *)g_hash_table_lookup(cfs_status
.iphash
, node
->name
);
315 g_string_append_printf(str
, ", \"ip\": \"%s\"", ip
);
318 g_string_append_printf(str
, "}");
321 g_string_append_printf(str
,"\n }\n");
323 g_string_append_printf(str
, "\"nodename\": \"%s\",\n", cfs
.nodename
);
324 g_string_append_printf(str
, "\"version\": %u\n", cfs_status
.clinfo_version
);
327 g_string_append_printf(str
,"}\n");
329 g_mutex_unlock (&mutex
);
335 kventry_free(kventry_t
*entry
)
337 g_return_if_fail(entry
!= NULL
);
345 kventry_hash_new(void)
347 return g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
,
348 (GDestroyNotify
)kventry_free
);
352 rrdentry_free(rrdentry_t
*entry
)
354 g_return_if_fail(entry
!= NULL
);
362 rrdentry_hash_new(void)
364 return g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
,
365 (GDestroyNotify
)rrdentry_free
);
369 cfs_cluster_log_dump(GString
*str
, const char *user
, guint max_entries
)
371 clusterlog_dump(cfs_status
.clusterlog
, str
, user
, max_entries
);
375 cfs_cluster_log(clog_entry_t
*entry
)
377 g_return_if_fail(entry
!= NULL
);
379 clusterlog_insert(cfs_status
.clusterlog
, entry
);
381 if (cfs_status
.kvstore
) {
383 iov
[0].iov_base
= (char *)entry
;
384 iov
[0].iov_len
= clog_entry_size(entry
);
386 if (dfsm_is_initialized(cfs_status
.kvstore
))
387 dfsm_send_message(cfs_status
.kvstore
, KVSTORE_MESSAGE_LOG
, iov
, 1);
391 void cfs_status_init(void)
393 g_mutex_lock (&mutex
);
395 cfs_status
.start_time
= time(NULL
);
397 cfs_status
.vmlist
= vmlist_hash_new();
399 cfs_status
.kvhash
= kventry_hash_new();
401 cfs_status
.rrdhash
= rrdentry_hash_new();
403 cfs_status
.iphash
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
405 cfs_status
.memdb_changes
= g_hash_table_new(g_str_hash
, g_str_equal
);
407 for (int i
= 0; i
< G_N_ELEMENTS(memdb_change_array
); i
++) {
408 g_hash_table_replace(cfs_status
.memdb_changes
,
409 memdb_change_array
[i
].path
,
410 &memdb_change_array
[i
]);
413 cfs_status
.clusterlog
= clusterlog_new();
416 clusterlog_add(cfs_status
.clusterlog
, "root", "cluster", getpid(),
417 LOG_INFO
, "starting cluster log");
419 g_mutex_unlock (&mutex
);
422 void cfs_status_cleanup(void)
424 g_mutex_lock (&mutex
);
426 cfs_status
.clinfo_version
++;
428 if (cfs_status
.clinfo
) {
429 cfs_clinfo_destroy(cfs_status
.clinfo
);
430 cfs_status
.clinfo
= NULL
;
433 if (cfs_status
.vmlist
) {
434 g_hash_table_destroy(cfs_status
.vmlist
);
435 cfs_status
.vmlist
= NULL
;
438 if (cfs_status
.kvhash
) {
439 g_hash_table_destroy(cfs_status
.kvhash
);
440 cfs_status
.kvhash
= NULL
;
443 if (cfs_status
.rrdhash
) {
444 g_hash_table_destroy(cfs_status
.rrdhash
);
445 cfs_status
.rrdhash
= NULL
;
448 if (cfs_status
.iphash
) {
449 g_hash_table_destroy(cfs_status
.iphash
);
450 cfs_status
.iphash
= NULL
;
453 if (cfs_status
.clusterlog
)
454 clusterlog_destroy(cfs_status
.clusterlog
);
456 g_mutex_unlock (&mutex
);
459 void cfs_status_set_clinfo(
460 cfs_clinfo_t
*clinfo
)
462 g_return_if_fail(clinfo
!= NULL
);
464 g_mutex_lock (&mutex
);
466 cfs_status
.clinfo_version
++;
468 cfs_clinfo_t
*old
= cfs_status
.clinfo
;
470 cfs_status
.clinfo
= clinfo
;
472 cfs_message("update cluster info (cluster name %s, version = %d)",
473 clinfo
->cluster_name
, clinfo
->cman_version
);
476 if (old
&& old
->nodes_byid
&& clinfo
->nodes_byid
) {
478 GHashTable
*ht
= clinfo
->nodes_byid
;
482 g_hash_table_iter_init (&iter
, ht
);
484 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
485 cfs_clnode_t
*node
= (cfs_clnode_t
*)value
;
486 cfs_clnode_t
*oldnode
;
487 if ((oldnode
= g_hash_table_lookup(old
->nodes_byid
, key
))) {
488 node
->online
= oldnode
->online
;
489 node
->kvhash
= oldnode
->kvhash
;
490 oldnode
->kvhash
= NULL
;
497 cfs_clinfo_destroy(old
);
500 g_mutex_unlock (&mutex
);
504 dump_kvstore_versions(
507 const char *nodename
)
509 g_return_if_fail(kvhash
!= NULL
);
510 g_return_if_fail(str
!= NULL
);
511 g_return_if_fail(nodename
!= NULL
);
513 GHashTable
*ht
= kvhash
;
517 g_string_append_printf(str
, "\"%s\": {\n", nodename
);
519 g_hash_table_iter_init (&iter
, ht
);
522 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
523 kventry_t
*entry
= (kventry_t
*)value
;
524 if (i
) g_string_append_printf(str
, ",\n");
526 g_string_append_printf(str
,"\"%s\": %u", entry
->key
, entry
->version
);
529 g_string_append_printf(str
, "}\n");
533 cfs_create_version_msg(GString
*str
)
535 g_return_val_if_fail(str
!= NULL
, -EINVAL
);
537 g_mutex_lock (&mutex
);
539 g_string_append_printf(str
,"{\n");
541 g_string_append_printf(str
, "\"starttime\": %lu,\n", (unsigned long)cfs_status
.start_time
);
543 g_string_append_printf(str
, "\"clinfo\": %u,\n", cfs_status
.clinfo_version
);
545 g_string_append_printf(str
, "\"vmlist\": %u,\n", cfs_status
.vmlist_version
);
547 for (int i
= 0; i
< G_N_ELEMENTS(memdb_change_array
); i
++) {
548 g_string_append_printf(str
, "\"%s\": %u,\n",
549 memdb_change_array
[i
].path
,
550 memdb_change_array
[i
].version
);
553 g_string_append_printf(str
, "\"kvstore\": {\n");
555 dump_kvstore_versions(str
, cfs_status
.kvhash
, cfs
.nodename
);
557 cfs_clinfo_t
*clinfo
= cfs_status
.clinfo
;
559 if (clinfo
&& clinfo
->nodes_byid
) {
560 GHashTable
*ht
= clinfo
->nodes_byid
;
564 g_hash_table_iter_init (&iter
, ht
);
566 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
567 cfs_clnode_t
*node
= (cfs_clnode_t
*)value
;
570 g_string_append_printf(str
, ",\n");
571 dump_kvstore_versions(str
, node
->kvhash
, node
->name
);
575 g_string_append_printf(str
,"}\n");
577 g_string_append_printf(str
,"}\n");
579 g_mutex_unlock (&mutex
);
585 vmlist_hash_new(void)
587 return g_hash_table_new_full(g_int_hash
, g_int_equal
, NULL
,
588 (GDestroyNotify
)vminfo_free
);
592 vmlist_hash_insert_vm(
596 const char *nodename
,
599 g_return_val_if_fail(vmlist
!= NULL
, FALSE
);
600 g_return_val_if_fail(nodename
!= NULL
, FALSE
);
601 g_return_val_if_fail(vmid
!= 0, FALSE
);
602 g_return_val_if_fail(vmtype
== VMTYPE_QEMU
|| vmtype
== VMTYPE_OPENVZ
||
603 vmtype
== VMTYPE_LXC
, FALSE
);
605 if (!replace
&& g_hash_table_lookup(vmlist
, &vmid
)) {
606 cfs_critical("detected duplicate VMID %d", vmid
);
610 vminfo_t
*vminfo
= g_new0(vminfo_t
, 1);
613 vminfo
->vmtype
= vmtype
;
614 vminfo
->nodename
= g_strdup(nodename
);
616 vminfo
->version
= ++vminfo_version_counter
;
618 g_hash_table_replace(vmlist
, &vminfo
->vmid
, vminfo
);
627 const char *nodename
)
629 g_return_if_fail(cfs_status
.vmlist
!= NULL
);
630 g_return_if_fail(nodename
!= NULL
);
631 g_return_if_fail(vmid
!= 0);
632 g_return_if_fail(vmtype
== VMTYPE_QEMU
|| vmtype
== VMTYPE_OPENVZ
||
633 vmtype
== VMTYPE_LXC
);
635 cfs_debug("vmlist_register_vm: %s/%u %d", nodename
, vmid
, vmtype
);
637 g_mutex_lock (&mutex
);
639 cfs_status
.vmlist_version
++;
641 vmlist_hash_insert_vm(cfs_status
.vmlist
, vmtype
, vmid
, nodename
, TRUE
);
643 g_mutex_unlock (&mutex
);
647 vmlist_different_vm_exists(
650 const char *nodename
)
652 g_return_val_if_fail(cfs_status
.vmlist
!= NULL
, FALSE
);
653 g_return_val_if_fail(vmid
!= 0, FALSE
);
655 gboolean res
= FALSE
;
657 g_mutex_lock (&mutex
);
660 if ((vminfo
= (vminfo_t
*)g_hash_table_lookup(cfs_status
.vmlist
, &vmid
))) {
661 if (!(vminfo
->vmtype
== vmtype
&& strcmp(vminfo
->nodename
, nodename
) == 0))
664 g_mutex_unlock (&mutex
);
673 g_return_val_if_fail(cfs_status
.vmlist
!= NULL
, FALSE
);
674 g_return_val_if_fail(vmid
!= 0, FALSE
);
676 g_mutex_lock (&mutex
);
678 gpointer res
= g_hash_table_lookup(cfs_status
.vmlist
, &vmid
);
680 g_mutex_unlock (&mutex
);
689 g_return_if_fail(cfs_status
.vmlist
!= NULL
);
690 g_return_if_fail(vmid
!= 0);
692 g_mutex_lock (&mutex
);
694 cfs_status
.vmlist_version
++;
696 g_hash_table_remove(cfs_status
.vmlist
, &vmid
);
698 g_mutex_unlock (&mutex
);
701 void cfs_status_set_vmlist(
704 g_return_if_fail(vmlist
!= NULL
);
706 g_mutex_lock (&mutex
);
708 cfs_status
.vmlist_version
++;
710 if (cfs_status
.vmlist
)
711 g_hash_table_destroy(cfs_status
.vmlist
);
713 cfs_status
.vmlist
= vmlist
;
715 g_mutex_unlock (&mutex
);
719 cfs_create_vmlist_msg(GString
*str
)
721 g_return_val_if_fail(cfs_status
.vmlist
!= NULL
, -EINVAL
);
722 g_return_val_if_fail(str
!= NULL
, -EINVAL
);
724 g_mutex_lock (&mutex
);
726 g_string_append_printf(str
,"{\n");
728 GHashTable
*ht
= cfs_status
.vmlist
;
730 guint count
= g_hash_table_size(ht
);
733 g_string_append_printf(str
,"\"version\": %u\n", cfs_status
.vmlist_version
);
735 g_string_append_printf(str
,"\"version\": %u,\n", cfs_status
.vmlist_version
);
737 g_string_append_printf(str
,"\"ids\": {\n");
742 g_hash_table_iter_init (&iter
, ht
);
745 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
746 vminfo_t
*vminfo
= (vminfo_t
*)value
;
747 const char *type
= vminfo_type_to_string(vminfo
);
750 g_string_append_printf(str
, ",\n");
753 g_string_append_printf(str
,"\"%u\": { \"node\": \"%s\", \"type\": \"%s\", \"version\": %u }",
754 vminfo
->vmid
, vminfo
->nodename
, type
, vminfo
->version
);
757 g_string_append_printf(str
,"}\n");
759 g_string_append_printf(str
,"\n}\n");
761 g_mutex_unlock (&mutex
);
767 record_memdb_change(const char *path
)
769 g_return_if_fail(cfs_status
.memdb_changes
!= 0);
773 if ((ce
= (memdb_change_t
*)g_hash_table_lookup(cfs_status
.memdb_changes
, path
))) {
779 record_memdb_reload(void)
781 for (int i
= 0; i
< G_N_ELEMENTS(memdb_change_array
); i
++) {
782 memdb_change_array
[i
].version
++;
793 g_return_val_if_fail(kvhash
!= NULL
, FALSE
);
794 g_return_val_if_fail(key
!= NULL
, FALSE
);
795 g_return_val_if_fail(data
!= NULL
, FALSE
);
799 g_hash_table_remove(kvhash
, key
);
800 } else if ((entry
= (kventry_t
*)g_hash_table_lookup(kvhash
, key
))) {
802 entry
->data
= g_memdup(data
, len
);
806 kventry_t
*entry
= g_new0(kventry_t
, 1);
808 entry
->key
= g_strdup(key
);
809 entry
->data
= g_memdup(data
, len
);
812 g_hash_table_replace(kvhash
, entry
->key
, entry
);
818 static const char *rrd_def_node
[] = {
819 "DS:loadavg:GAUGE:120:0:U",
820 "DS:maxcpu:GAUGE:120:0:U",
821 "DS:cpu:GAUGE:120:0:U",
822 "DS:iowait:GAUGE:120:0:U",
823 "DS:memtotal:GAUGE:120:0:U",
824 "DS:memused:GAUGE:120:0:U",
825 "DS:swaptotal:GAUGE:120:0:U",
826 "DS:swapused:GAUGE:120:0:U",
827 "DS:roottotal:GAUGE:120:0:U",
828 "DS:rootused:GAUGE:120:0:U",
829 "DS:netin:DERIVE:120:0:U",
830 "DS:netout:DERIVE:120:0:U",
832 "RRA:AVERAGE:0.5:1:70", // 1 min avg - one hour
833 "RRA:AVERAGE:0.5:30:70", // 30 min avg - one day
834 "RRA:AVERAGE:0.5:180:70", // 3 hour avg - one week
835 "RRA:AVERAGE:0.5:720:70", // 12 hour avg - one month
836 "RRA:AVERAGE:0.5:10080:70", // 7 day avg - ony year
838 "RRA:MAX:0.5:1:70", // 1 min max - one hour
839 "RRA:MAX:0.5:30:70", // 30 min max - one day
840 "RRA:MAX:0.5:180:70", // 3 hour max - one week
841 "RRA:MAX:0.5:720:70", // 12 hour max - one month
842 "RRA:MAX:0.5:10080:70", // 7 day max - ony year
846 static const char *rrd_def_vm
[] = {
847 "DS:maxcpu:GAUGE:120:0:U",
848 "DS:cpu:GAUGE:120:0:U",
849 "DS:maxmem:GAUGE:120:0:U",
850 "DS:mem:GAUGE:120:0:U",
851 "DS:maxdisk:GAUGE:120:0:U",
852 "DS:disk:GAUGE:120:0:U",
853 "DS:netin:DERIVE:120:0:U",
854 "DS:netout:DERIVE:120:0:U",
855 "DS:diskread:DERIVE:120:0:U",
856 "DS:diskwrite:DERIVE:120:0:U",
858 "RRA:AVERAGE:0.5:1:70", // 1 min avg - one hour
859 "RRA:AVERAGE:0.5:30:70", // 30 min avg - one day
860 "RRA:AVERAGE:0.5:180:70", // 3 hour avg - one week
861 "RRA:AVERAGE:0.5:720:70", // 12 hour avg - one month
862 "RRA:AVERAGE:0.5:10080:70", // 7 day avg - ony year
864 "RRA:MAX:0.5:1:70", // 1 min max - one hour
865 "RRA:MAX:0.5:30:70", // 30 min max - one day
866 "RRA:MAX:0.5:180:70", // 3 hour max - one week
867 "RRA:MAX:0.5:720:70", // 12 hour max - one month
868 "RRA:MAX:0.5:10080:70", // 7 day max - ony year
872 static const char *rrd_def_storage
[] = {
873 "DS:total:GAUGE:120:0:U",
874 "DS:used:GAUGE:120:0:U",
876 "RRA:AVERAGE:0.5:1:70", // 1 min avg - one hour
877 "RRA:AVERAGE:0.5:30:70", // 30 min avg - one day
878 "RRA:AVERAGE:0.5:180:70", // 3 hour avg - one week
879 "RRA:AVERAGE:0.5:720:70", // 12 hour avg - one month
880 "RRA:AVERAGE:0.5:10080:70", // 7 day avg - ony year
882 "RRA:MAX:0.5:1:70", // 1 min max - one hour
883 "RRA:MAX:0.5:30:70", // 30 min max - one day
884 "RRA:MAX:0.5:180:70", // 3 hour max - one week
885 "RRA:MAX:0.5:720:70", // 12 hour max - one month
886 "RRA:MAX:0.5:10080:70", // 7 day max - ony year
890 #define RRDDIR "/var/lib/rrdcached/db"
894 const char *filename
,
896 const char *rrddef
[])
898 /* start at day boundary */
901 struct tm
*ltm
= localtime(&ctime
);
907 if (rrd_create_r(filename
, 60, timelocal(ltm
), argcount
, rrddef
)) {
908 cfs_message("RRD create error %s: %s", filename
, rrd_get_error());
912 static inline const char *
918 while (*data
&& found
< count
) {
931 g_return_if_fail(key
!= NULL
);
932 g_return_if_fail(data
!= NULL
);
933 g_return_if_fail(len
> 0);
934 g_return_if_fail(len
< 4096);
936 static const char *rrdcsock
= "unix:/var/run/rrdcached.sock";
939 if (rrdc_connect(rrdcsock
) != 0)
942 char *filename
= NULL
;
946 if (strncmp(key
, "pve2-node/", 10) == 0) {
947 const char *node
= key
+ 10;
951 if (strchr(node
, '/') != NULL
)
954 if (strlen(node
) < 1)
957 filename
= g_strdup_printf(RRDDIR
"/%s", key
);
959 if (!g_file_test(filename
, G_FILE_TEST_EXISTS
)) {
961 mkdir(RRDDIR
"/pve2-node", 0755);
962 int argcount
= sizeof(rrd_def_node
)/sizeof(void*) - 1;
963 create_rrd_file(filename
, argcount
, rrd_def_node
);
966 } else if ((strncmp(key
, "pve2-vm/", 8) == 0) ||
967 (strncmp(key
, "pve2.3-vm/", 10) == 0)) {
970 if (strncmp(key
, "pve2-vm/", 8) == 0) {
978 if (strchr(vmid
, '/') != NULL
)
981 if (strlen(vmid
) < 1)
984 filename
= g_strdup_printf(RRDDIR
"/%s/%s", "pve2-vm", vmid
);
986 if (!g_file_test(filename
, G_FILE_TEST_EXISTS
)) {
988 mkdir(RRDDIR
"/pve2-vm", 0755);
989 int argcount
= sizeof(rrd_def_vm
)/sizeof(void*) - 1;
990 create_rrd_file(filename
, argcount
, rrd_def_vm
);
993 } else if (strncmp(key
, "pve2-storage/", 13) == 0) {
994 const char *node
= key
+ 13;
996 const char *storage
= node
;
997 while (*storage
&& *storage
!= '/')
1000 if (*storage
!= '/' || ((storage
- node
) < 1))
1005 if (strchr(storage
, '/') != NULL
)
1008 if (strlen(storage
) < 1)
1011 filename
= g_strdup_printf(RRDDIR
"/%s", key
);
1013 if (!g_file_test(filename
, G_FILE_TEST_EXISTS
)) {
1015 mkdir(RRDDIR
"/pve2-storage", 0755);
1017 char *dir
= g_path_get_dirname(filename
);
1021 int argcount
= sizeof(rrd_def_storage
)/sizeof(void*) - 1;
1022 create_rrd_file(filename
, argcount
, rrd_def_storage
);
1029 const char *dp
= skip
? rrd_skip_data(data
, skip
) : data
;
1031 const char *update_args
[] = { dp
, NULL
};
1035 if ((status
= rrdc_update(filename
, 1, update_args
)) != 0) {
1036 cfs_message("RRDC update error %s: %d", filename
, status
);
1039 if (rrd_update_r(filename
, NULL
, 1, update_args
) != 0) {
1040 cfs_message("RRD update error %s: %s", filename
, rrd_get_error());
1046 if (rrd_update_r(filename
, NULL
, 1, update_args
) != 0) {
1047 cfs_message("RRD update error %s: %s", filename
, rrd_get_error());
1058 cfs_critical("RRD update error: unknown/wrong key %s", key
);
1068 rrdentry_t
*entry
= (rrdentry_t
*)value
;
1069 uint32_t ctime
= GPOINTER_TO_UINT(user_data
);
1071 int diff
= ctime
- entry
->time
;
1073 /* remove everything older than 5 minutes */
1076 return (diff
> expire
) ? TRUE
: FALSE
;
1079 static char *rrd_dump_buf
= NULL
;
1080 static time_t rrd_dump_last
= 0;
1083 cfs_rrd_dump(GString
*str
)
1087 g_mutex_lock (&mutex
);
1090 if (rrd_dump_buf
&& (ctime
- rrd_dump_last
) < 2) {
1091 g_string_assign(str
, rrd_dump_buf
);
1092 g_mutex_unlock (&mutex
);
1096 /* remove old data */
1097 g_hash_table_foreach_remove(cfs_status
.rrdhash
, rrd_entry_is_old
,
1098 GUINT_TO_POINTER(ctime
));
1100 g_string_set_size(str
, 0);
1102 GHashTableIter iter
;
1103 gpointer key
, value
;
1105 g_hash_table_iter_init (&iter
, cfs_status
.rrdhash
);
1107 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
1108 rrdentry_t
*entry
= (rrdentry_t
*)value
;
1109 g_string_append(str
, key
);
1110 g_string_append(str
, ":");
1111 g_string_append(str
, entry
->data
);
1112 g_string_append(str
, "\n");
1115 g_string_append_c(str
, 0); // never return undef
1117 rrd_dump_last
= ctime
;
1119 g_free(rrd_dump_buf
);
1120 rrd_dump_buf
= g_strdup(str
->str
);
1122 g_mutex_unlock (&mutex
);
1128 const char *nodename
,
1132 g_return_val_if_fail(iphash
!= NULL
, FALSE
);
1133 g_return_val_if_fail(nodename
!= NULL
, FALSE
);
1134 g_return_val_if_fail(ip
!= NULL
, FALSE
);
1135 g_return_val_if_fail(len
> 0, FALSE
);
1136 g_return_val_if_fail(len
< 256, FALSE
);
1137 g_return_val_if_fail(ip
[len
-1] == 0, FALSE
);
1139 char *oldip
= (char *)g_hash_table_lookup(iphash
, nodename
);
1141 if (!oldip
|| (strcmp(oldip
, ip
) != 0)) {
1142 cfs_status
.clinfo_version
++;
1143 g_hash_table_replace(iphash
, g_strdup(nodename
), g_strdup(ip
));
1151 GHashTable
*rrdhash
,
1156 g_return_val_if_fail(rrdhash
!= NULL
, FALSE
);
1157 g_return_val_if_fail(key
!= NULL
, FALSE
);
1158 g_return_val_if_fail(data
!= NULL
, FALSE
);
1159 g_return_val_if_fail(len
> 0, FALSE
);
1160 g_return_val_if_fail(len
< 4096, FALSE
);
1161 g_return_val_if_fail(data
[len
-1] == 0, FALSE
);
1164 if ((entry
= (rrdentry_t
*)g_hash_table_lookup(rrdhash
, key
))) {
1165 g_free(entry
->data
);
1166 entry
->data
= g_memdup(data
, len
);
1168 entry
->time
= time(NULL
);
1170 rrdentry_t
*entry
= g_new0(rrdentry_t
, 1);
1172 entry
->key
= g_strdup(key
);
1173 entry
->data
= g_memdup(data
, len
);
1175 entry
->time
= time(NULL
);
1177 g_hash_table_replace(rrdhash
, entry
->key
, entry
);
1180 update_rrd_data(key
, data
, len
);
1186 kvstore_send_update_message(
1192 if (!dfsm_is_initialized(dfsm
))
1195 struct iovec iov
[2];
1198 g_strlcpy(name
, key
, sizeof(name
));
1200 iov
[0].iov_base
= &name
;
1201 iov
[0].iov_len
= sizeof(name
);
1203 iov
[1].iov_base
= (char *)data
;
1204 iov
[1].iov_len
= len
;
1206 if (dfsm_send_message(dfsm
, KVSTORE_MESSAGE_UPDATE
, iov
, 2) == CS_OK
)
1212 static clog_entry_t
*
1213 kvstore_parse_log_message(
1217 g_return_val_if_fail(msg
!= NULL
, NULL
);
1219 if (msg_len
< sizeof(clog_entry_t
)) {
1220 cfs_critical("received short log message (%zu < %zu)", msg_len
, sizeof(clog_entry_t
));
1224 clog_entry_t
*entry
= (clog_entry_t
*)msg
;
1226 uint32_t size
= sizeof(clog_entry_t
) + entry
->node_len
+
1227 entry
->ident_len
+ entry
->tag_len
+ entry
->msg_len
;
1229 if (msg_len
!= size
) {
1230 cfs_critical("received log message with wrong size (%zu != %u)", msg_len
, size
);
1236 if (*((char *)msg
+ entry
->node_len
- 1)) {
1237 cfs_critical("unterminated string in log message");
1240 msg
+= entry
->node_len
;
1242 if (*((char *)msg
+ entry
->ident_len
- 1)) {
1243 cfs_critical("unterminated string in log message");
1246 msg
+= entry
->ident_len
;
1248 if (*((char *)msg
+ entry
->tag_len
- 1)) {
1249 cfs_critical("unterminated string in log message");
1252 msg
+= entry
->tag_len
;
1254 if (*((char *)msg
+ entry
->msg_len
- 1)) {
1255 cfs_critical("unterminated string in log message");
1263 kvstore_parse_update_message(
1267 gconstpointer
*data
,
1270 g_return_val_if_fail(msg
!= NULL
, FALSE
);
1271 g_return_val_if_fail(key
!= NULL
, FALSE
);
1272 g_return_val_if_fail(data
!= NULL
, FALSE
);
1273 g_return_val_if_fail(len
!= NULL
, FALSE
);
1275 if (msg_len
< 256) {
1276 cfs_critical("received short kvstore message (%zu < 256)", msg_len
);
1280 /* test if key is null terminated */
1282 for (i
= 0; i
< 256; i
++)
1283 if (((char *)msg
)[i
] == 0)
1290 *len
= msg_len
- 256;
1298 cfs_create_status_msg(
1300 const char *nodename
,
1303 g_return_val_if_fail(str
!= NULL
, -EINVAL
);
1304 g_return_val_if_fail(key
!= NULL
, -EINVAL
);
1308 GHashTable
*kvhash
= NULL
;
1310 g_mutex_lock (&mutex
);
1312 if (!nodename
|| !nodename
[0] || !strcmp(nodename
, cfs
.nodename
)) {
1313 kvhash
= cfs_status
.kvhash
;
1314 } else if (cfs_status
.clinfo
&& cfs_status
.clinfo
->nodes_byname
) {
1315 cfs_clnode_t
*clnode
;
1316 if ((clnode
= g_hash_table_lookup(cfs_status
.clinfo
->nodes_byname
, nodename
)))
1317 kvhash
= clnode
->kvhash
;
1321 if (kvhash
&& (entry
= (kventry_t
*)g_hash_table_lookup(kvhash
, key
))) {
1322 g_string_append_len(str
, entry
->data
, entry
->len
);
1326 g_mutex_unlock (&mutex
);
1337 g_return_val_if_fail(key
!= NULL
, FALSE
);
1338 g_return_val_if_fail(data
!= NULL
, FALSE
);
1339 g_return_val_if_fail(cfs_status
.kvhash
!= NULL
, FALSE
);
1341 if (len
> CFS_MAX_STATUS_SIZE
)
1344 g_mutex_lock (&mutex
);
1348 if (strncmp(key
, "rrd/", 4) == 0) {
1349 res
= rrdentry_hash_set(cfs_status
.rrdhash
, key
+ 4, data
, len
);
1350 } else if (!strcmp(key
, "nodeip")) {
1351 res
= nodeip_hash_set(cfs_status
.iphash
, cfs
.nodename
, data
, len
);
1353 res
= kventry_hash_set(cfs_status
.kvhash
, key
, data
, len
);
1355 g_mutex_unlock (&mutex
);
1357 if (cfs_status
.kvstore
)
1358 kvstore_send_update_message(cfs_status
.kvstore
, key
, data
, len
);
1360 return res
? 0 : -ENOMEM
;
1364 cfs_kvstore_node_set(
1370 g_return_val_if_fail(nodeid
!= 0, FALSE
);
1371 g_return_val_if_fail(key
!= NULL
, FALSE
);
1372 g_return_val_if_fail(data
!= NULL
, FALSE
);
1374 g_mutex_lock (&mutex
);
1376 if (!cfs_status
.clinfo
|| !cfs_status
.clinfo
->nodes_byid
)
1377 goto ret
; /* ignore */
1379 cfs_clnode_t
*clnode
= g_hash_table_lookup(cfs_status
.clinfo
->nodes_byid
, &nodeid
);
1381 goto ret
; /* ignore */
1383 cfs_debug("got node %d status update %s", nodeid
, key
);
1385 if (strncmp(key
, "rrd/", 4) == 0) {
1386 rrdentry_hash_set(cfs_status
.rrdhash
, key
+ 4, data
, len
);
1387 } else if (!strcmp(key
, "nodeip")) {
1388 nodeip_hash_set(cfs_status
.iphash
, clnode
->name
, data
, len
);
1390 if (!clnode
->kvhash
) {
1391 if (!(clnode
->kvhash
= kventry_hash_new())) {
1392 goto ret
; /*ignore */
1396 kventry_hash_set(clnode
->kvhash
, key
, data
, len
);
1400 g_mutex_unlock (&mutex
);
1406 cfs_kvstore_sync(void)
1408 g_return_val_if_fail(cfs_status
.kvhash
!= NULL
, FALSE
);
1409 g_return_val_if_fail(cfs_status
.kvstore
!= NULL
, FALSE
);
1411 gboolean res
= TRUE
;
1413 g_mutex_lock (&mutex
);
1415 GHashTable
*ht
= cfs_status
.kvhash
;
1416 GHashTableIter iter
;
1417 gpointer key
, value
;
1419 g_hash_table_iter_init (&iter
, ht
);
1421 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
1422 kventry_t
*entry
= (kventry_t
*)value
;
1423 kvstore_send_update_message(cfs_status
.kvstore
, entry
->key
, entry
->data
, entry
->len
);
1426 g_mutex_unlock (&mutex
);
1443 g_return_val_if_fail(dfsm
!= NULL
, -1);
1444 g_return_val_if_fail(msg
!= NULL
, -1);
1445 g_return_val_if_fail(res_ptr
!= NULL
, -1);
1447 /* ignore message for ourself */
1448 if (dfsm_nodeid_is_local(dfsm
, nodeid
, pid
))
1451 if (msg_type
== KVSTORE_MESSAGE_UPDATE
) {
1455 if (kvstore_parse_update_message(msg
, msg_len
, &key
, &data
, &len
)) {
1456 cfs_kvstore_node_set(nodeid
, key
, data
, len
);
1458 cfs_critical("cant parse update message");
1460 } else if (msg_type
== KVSTORE_MESSAGE_LOG
) {
1461 cfs_message("received log"); // fixme: remove
1462 const clog_entry_t
*entry
;
1463 if ((entry
= kvstore_parse_log_message(msg
, msg_len
))) {
1464 clusterlog_insert(cfs_status
.clusterlog
, entry
);
1466 cfs_critical("cant parse log message");
1469 cfs_critical("received unknown message type %d\n", msg_type
);
1486 const struct cpg_address
*member_list
,
1487 size_t member_list_entries
)
1489 g_return_if_fail(dfsm
!= NULL
);
1490 g_return_if_fail(member_list
!= NULL
);
1492 cfs_debug("enter %s", __func__
);
1494 g_mutex_lock (&mutex
);
1496 cfs_clinfo_t
*clinfo
= cfs_status
.clinfo
;
1498 if (clinfo
&& clinfo
->nodes_byid
) {
1500 GHashTable
*ht
= clinfo
->nodes_byid
;
1501 GHashTableIter iter
;
1502 gpointer key
, value
;
1504 g_hash_table_iter_init (&iter
, ht
);
1506 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
1507 cfs_clnode_t
*node
= (cfs_clnode_t
*)value
;
1508 node
->online
= FALSE
;
1511 for (int i
= 0; i
< member_list_entries
; i
++) {
1513 if ((node
= g_hash_table_lookup(clinfo
->nodes_byid
, &member_list
[i
].nodeid
))) {
1514 node
->online
= TRUE
;
1518 cfs_status
.clinfo_version
++;
1521 g_mutex_unlock (&mutex
);
1528 unsigned int *res_len
)
1530 g_return_val_if_fail(dfsm
!= NULL
, NULL
);
1532 gpointer msg
= clusterlog_get_state(cfs_status
.clusterlog
, res_len
);
1538 dfsm_process_update(
1541 dfsm_sync_info_t
*syncinfo
,
1547 cfs_critical("%s: received unexpected update message", __func__
);
1553 dfsm_process_state_update(
1556 dfsm_sync_info_t
*syncinfo
)
1558 g_return_val_if_fail(dfsm
!= NULL
, -1);
1559 g_return_val_if_fail(syncinfo
!= NULL
, -1);
1561 clog_base_t
*clog
[syncinfo
->node_count
];
1563 int local_index
= -1;
1564 for (int i
= 0; i
< syncinfo
->node_count
; i
++) {
1565 dfsm_node_info_t
*ni
= &syncinfo
->nodes
[i
];
1568 if (syncinfo
->local
== ni
)
1571 clog_base_t
*base
= (clog_base_t
*)ni
->state
;
1572 if (ni
->state_len
> 8 && ni
->state_len
== clog_size(base
)) {
1573 clog
[i
] = ni
->state
;
1575 cfs_critical("received log with wrong size %u", ni
->state_len
);
1580 if (!clusterlog_merge(cfs_status
.clusterlog
, clog
, syncinfo
->node_count
, local_index
)) {
1581 cfs_critical("unable to merge log files");
1593 dfsm_sync_info_t
*syncinfo
)
1595 g_return_val_if_fail(dfsm
!= NULL
, -1);
1596 g_return_val_if_fail(syncinfo
!= NULL
, -1);
1602 dfsm_synced(dfsm_t
*dfsm
)
1604 g_return_if_fail(dfsm
!= NULL
);
1606 char *ip
= (char *)g_hash_table_lookup(cfs_status
.iphash
, cfs
.nodename
);
1610 cfs_status_set("nodeip", ip
, strlen(ip
) + 1);
1617 dfsm_sync_info_t
*syncinfo
)
1622 static dfsm_callbacks_t kvstore_dfsm_callbacks
= {
1623 .dfsm_deliver_fn
= dfsm_deliver
,
1624 .dfsm_confchg_fn
= dfsm_confchg
,
1626 .dfsm_get_state_fn
= dfsm_get_state
,
1627 .dfsm_process_state_update_fn
= dfsm_process_state_update
,
1628 .dfsm_process_update_fn
= dfsm_process_update
,
1629 .dfsm_commit_fn
= dfsm_commit
,
1630 .dfsm_cleanup_fn
= dfsm_cleanup
,
1631 .dfsm_synced_fn
= dfsm_synced
,
1635 cfs_status_dfsm_new(void)
1637 g_mutex_lock (&mutex
);
1639 cfs_status
.kvstore
= dfsm_new(NULL
, KVSTORE_CPG_GROUP_NAME
, G_LOG_DOMAIN
,
1640 0, &kvstore_dfsm_callbacks
);
1641 g_mutex_unlock (&mutex
);
1643 return cfs_status
.kvstore
;
1647 cfs_is_quorate(void)
1649 g_mutex_lock (&mutex
);
1650 gboolean res
= cfs_status
.quorate
;
1651 g_mutex_unlock (&mutex
);
1661 g_mutex_lock (&mutex
);
1663 uint32_t prev_quorate
= cfs_status
.quorate
;
1664 cfs_status
.quorate
= quorate
;
1666 if (!prev_quorate
&& cfs_status
.quorate
) {
1668 cfs_message("node has quorum");
1671 if (prev_quorate
&& !cfs_status
.quorate
) {
1673 cfs_message("node lost quorum");
1676 g_mutex_unlock (&mutex
);