@cryptotaxi247 / netdata-1 / commits / 9bedfb4d9

proc netstat: add network interface statistics function (#16334)

* add network interface stats function * move Total to 3rd pos * fix drops tx

Ilya Mashchenko committed Nov 4, 2023 at 14:12 UTC 9bedfb4d9521e73edff0fe8e3b1de127392b5219
1 file changed +338
collectors/proc.plugin/proc_net_dev.c
+338
@@ -5,6 +5,8 @@
5 #define PLUGIN_PROC_MODULE_NETDEV_NAME "/proc/net/dev"
6 #define CONFIG_SECTION_PLUGIN_PROC_NETDEV "plugin:" PLUGIN_PROC_CONFIG_NAME ":" PLUGIN_PROC_MODULE_NETDEV_NAME
7
8 +#define RRDFUNCTIONS_NETDEV_HELP "View network interface statistics"
9 +
10 #define STATE_LENGTH_MAX 32
11
12 #define READ_RETRY_PERIOD 60 // seconds
@@ -20,6 +22,18 @@ enum {
22 NETDEV_DUPLEX_FULL
23 };
24
25 +static const char *get_duplex_string(int duplex)
26 +{
27 + switch (duplex) {
28 + case NETDEV_DUPLEX_FULL:
29 + return "full";
30 + case NETDEV_DUPLEX_HALF:
31 + return "half";
32 + default:
33 + return "unknown";
34 + }
35 +}
36 +
37 enum {
38 NETDEV_OPERSTATE_UNKNOWN,
39 NETDEV_OPERSTATE_NOTPRESENT,
@@ -49,6 +63,26 @@ static inline int get_operstate(char *operstate)
63 return NETDEV_OPERSTATE_UNKNOWN;
64 }
65
66 +static const char *get_operstate_string(int operstate)
67 +{
68 + switch (operstate) {
69 + case NETDEV_OPERSTATE_UP:
70 + return "up";
71 + case NETDEV_OPERSTATE_DOWN:
72 + return "down";
73 + case NETDEV_OPERSTATE_NOTPRESENT:
74 + return "notpresent";
75 + case NETDEV_OPERSTATE_LOWERLAYERDOWN:
76 + return "lowerlayerdown";
77 + case NETDEV_OPERSTATE_TESTING:
78 + return "testing";
79 + case NETDEV_OPERSTATE_DORMANT:
80 + return "dormant";
81 + default:
82 + return "unknown";
83 + }
84 +}
85 +
86 // ----------------------------------------------------------------------------
87 // netdev list
88
@@ -63,6 +97,8 @@ static struct netdev {
97 int enabled;
98 int updated;
99
100 + bool function_ready;
101 +
102 time_t discover_time;
103
104 int carrier_file_exists;
@@ -367,6 +403,7 @@ static struct netdev_rename {
403
404 static int netdev_pending_renames = 0;
405 static netdata_mutex_t netdev_rename_mutex = NETDATA_MUTEX_INITIALIZER;
406 +static netdata_mutex_t netdev_dev_mutex = NETDATA_MUTEX_INITIALIZER;
407
408 static struct netdev_rename *netdev_rename_find(const char *host_device, uint32_t hash) {
409 struct netdev_rename *r;
@@ -572,6 +609,297 @@ static inline void netdev_rename_all_lock(void) {
609 }
610
611 // ----------------------------------------------------------------------------
612 +
613 +int netdev_function_net_interfaces(BUFFER *wb, int timeout __maybe_unused, const char *function __maybe_unused,
614 + void *collector_data __maybe_unused,
615 + rrd_function_result_callback_t result_cb, void *result_cb_data,
616 + rrd_function_is_cancelled_cb_t is_cancelled_cb, void *is_cancelled_cb_data,
617 + rrd_function_register_canceller_cb_t register_canceller_cb __maybe_unused,
618 + void *register_canceller_cb_data __maybe_unused) {
619 +
620 + buffer_flush(wb);
621 + wb->content_type = CT_APPLICATION_JSON;
622 + buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT);
623 +
624 + buffer_json_member_add_string(wb, "hostname", rrdhost_hostname(localhost));
625 + buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
626 + buffer_json_member_add_string(wb, "type", "table");
627 + buffer_json_member_add_time_t(wb, "update_every", 1);
628 + buffer_json_member_add_string(wb, "help", RRDFUNCTIONS_NETDEV_HELP);
629 + buffer_json_member_add_array(wb, "data");
630 +
631 + double max_traffic_rx = 0.0;
632 + double max_traffic_tx = 0.0;
633 + double max_traffic = 0.0;
634 + double max_packets_rx = 0.0;
635 + double max_packets_tx = 0.0;
636 + double max_mcast_rx = 0.0;
637 + double max_drops_rx = 0.0;
638 + double max_drops_tx = 0.0;
639 +
640 + netdata_mutex_lock(&netdev_dev_mutex);
641 +
642 + RRDDIM *rd = NULL;
643 +
644 + for (struct netdev *d = netdev_root; d != netdev_last_used; d = d->next) {
645 + if (unlikely(!d->function_ready))
646 + continue;
647 +
648 + buffer_json_add_array_item_array(wb);
649 +
650 + buffer_json_add_array_item_string(wb, d->name);
651 +
652 + buffer_json_add_array_item_string(wb, d->virtual ? "virtual" : "physical");
653 + buffer_json_add_array_item_string(wb, d->carrier == 1 ? "up" : "down");
654 + buffer_json_add_array_item_string(wb, get_operstate_string(d->operstate));
655 + buffer_json_add_array_item_string(wb, get_duplex_string(d->duplex));
656 + buffer_json_add_array_item_double(wb, d->speed > 0 ? d->speed : NAN);
657 + buffer_json_add_array_item_double(wb, d->mtu > 0 ? d->mtu : NAN);
658 +
659 + rd = d->flipped ? d->rd_tbytes : d->rd_rbytes;
660 + double traffic_rx = NAN;
661 + if (rd) {
662 + traffic_rx = rd->collector.last_stored_value / 1000.0;
663 + max_traffic_rx = MAX(max_traffic_rx, traffic_rx);
664 + }
665 +
666 + rd = d->flipped ? d->rd_rbytes : d->rd_tbytes;
667 + double traffic_tx = NAN;
668 + if (rd) {
669 + traffic_tx = ABS(rd->collector.last_stored_value / 1000.0);
670 + max_traffic_tx = MAX(max_traffic_tx, traffic_tx);
671 + }
672 +
673 + // FIXME: "traffic" (total) is needed only for default_sorting
674 + // can be removed when default_sorting will accept multiple columns (sum)
675 + double traffic = NAN;
676 + if (!isnan(traffic_rx) && !isnan(traffic_tx)) {
677 + traffic = traffic_rx + traffic_tx;
678 + max_traffic = MAX(max_traffic, traffic);
679 + }
680 +
681 + buffer_json_add_array_item_double(wb, traffic_rx);
682 + buffer_json_add_array_item_double(wb, traffic_tx);
683 + buffer_json_add_array_item_double(wb, traffic);
684 +
685 + rd = d->flipped ? d->rd_tpackets : d->rd_rpackets;
686 + double packets_rx = NAN;
687 + if (rd) {
688 + packets_rx = rd->collector.last_stored_value / 1000.0;
689 + max_packets_rx = MAX(max_packets_rx, packets_rx);
690 + }
691 + buffer_json_add_array_item_double(wb, packets_rx);
692 +
693 + rd = d->flipped ? d->rd_rpackets : d->rd_tpackets;
694 + double packets_tx = NAN;
695 + if (rd) {
696 + packets_tx = ABS(rd->collector.last_stored_value / 1000.0);
697 + max_packets_tx = MAX(max_packets_tx, packets_tx);
698 + }
699 + buffer_json_add_array_item_double(wb, packets_tx);
700 +
701 + double mcast_rx = NAN;
702 + if (d->rd_rmulticast) {
703 + mcast_rx = ABS(d->rd_rmulticast->collector.last_stored_value / 1000.0);
704 + max_mcast_rx = MAX(max_mcast_rx, mcast_rx);
705 + }
706 + buffer_json_add_array_item_double(wb, mcast_rx);
707 +
708 +
709 + rd = d->flipped ? d->rd_tdrops : d->rd_rdrops;
710 + double drops_rx = NAN;
711 + if (rd) {
712 + drops_rx = rd->collector.last_stored_value;
713 + max_drops_rx = MAX(max_drops_rx, drops_rx);
714 + }
715 + buffer_json_add_array_item_double(wb, drops_rx);
716 +
717 + rd = d->flipped ? d->rd_rdrops : d->rd_tdrops;
718 + double drops_tx = NAN;
719 + if (rd) {
720 + drops_tx = ABS(rd->collector.last_stored_value);
721 + max_drops_tx = MAX(max_drops_tx, drops_tx);
722 + }
723 + buffer_json_add_array_item_double(wb, drops_tx);
724 +
725 + buffer_json_array_close(wb);
726 + }
727 +
728 + netdata_mutex_unlock(&netdev_dev_mutex);
729 +
730 + buffer_json_array_close(wb); // data
731 + buffer_json_member_add_object(wb, "columns");
732 + {
733 + size_t field_id = 0;
734 +
735 + buffer_rrdf_table_add_field(wb, field_id++, "Name", "Network Interface Name",
736 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
737 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
738 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
739 + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_STICKY,
740 + NULL);
741 +
742 + buffer_rrdf_table_add_field(wb, field_id++, "Type", "Network Interface Type",
743 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
744 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
745 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
746 + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY,
747 + NULL);
748 +
749 + buffer_rrdf_table_add_field(wb, field_id++, "PhState", "Current Physical State",
750 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
751 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
752 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
753 + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY,
754 + NULL);
755 +
756 + buffer_rrdf_table_add_field(wb, field_id++, "OpState", "Current Operational State",
757 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
758 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
759 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
760 + RRDF_FIELD_OPTS_UNIQUE_KEY,
761 + NULL);
762 +
763 + buffer_rrdf_table_add_field(wb, field_id++, "Duplex", "Current Duplex Mode",
764 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
765 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
766 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
767 + RRDF_FIELD_OPTS_UNIQUE_KEY,
768 + NULL);
769 +
770 + buffer_rrdf_table_add_field(wb, field_id++, "Speed", "Current Link Speed",
771 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
772 + 0, "Mbit", NAN, RRDF_FIELD_SORT_DESCENDING, NULL,
773 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
774 + RRDF_FIELD_OPTS_UNIQUE_KEY,
775 + NULL);
776 +
777 + buffer_rrdf_table_add_field(wb, field_id++, "MTU", "Maximum Transmission Unit",
778 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
779 + 0, "Octets", NAN, RRDF_FIELD_SORT_DESCENDING, NULL,
780 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
781 + RRDF_FIELD_OPTS_UNIQUE_KEY,
782 + NULL);
783 +
784 + buffer_rrdf_table_add_field(wb, field_id++, "In", "Traffic Received",
785 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
786 + 2, "MiB", max_traffic_rx, RRDF_FIELD_SORT_DESCENDING, NULL,
787 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
788 + RRDF_FIELD_OPTS_VISIBLE,
789 + NULL);
790 +
791 + buffer_rrdf_table_add_field(wb, field_id++, "Out", "Traffic Sent",
792 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
793 + 2, "MiB", max_traffic_tx, RRDF_FIELD_SORT_DESCENDING, NULL,
794 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
795 + RRDF_FIELD_OPTS_VISIBLE,
796 + NULL);
797 +
798 + buffer_rrdf_table_add_field(wb, field_id++, "Total", "Traffic Received and Sent",
799 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
800 + 2, "MiB", max_traffic, RRDF_FIELD_SORT_DESCENDING, NULL,
801 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
802 + RRDF_FIELD_OPTS_VISIBLE,
803 + NULL);
804 +
805 + buffer_rrdf_table_add_field(wb, field_id++, "PktsIn", "Received Packets",
806 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
807 + 2, "Kpps", max_packets_rx, RRDF_FIELD_SORT_DESCENDING, NULL,
808 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
809 + RRDF_FIELD_OPTS_VISIBLE,
810 + NULL);
811 +
812 + buffer_rrdf_table_add_field(wb, field_id++, "PktsOut", "Sent Packets",
813 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
814 + 2, "Kpps", max_packets_tx, RRDF_FIELD_SORT_DESCENDING, NULL,
815 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
816 + RRDF_FIELD_OPTS_VISIBLE,
817 + NULL);
818 +
819 + buffer_rrdf_table_add_field(wb, field_id++, "McastIn", "Multicast Received Packets",
820 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
821 + 2, "Kpps", max_mcast_rx, RRDF_FIELD_SORT_DESCENDING, NULL,
822 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
823 + RRDF_FIELD_OPTS_NONE,
824 + NULL);
825 +
826 + buffer_rrdf_table_add_field(wb, field_id++, "DropsIn", "Dropped Inbound Packets",
827 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
828 + 2, "Drops", max_drops_rx, RRDF_FIELD_SORT_DESCENDING, NULL,
829 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
830 + RRDF_FIELD_OPTS_VISIBLE,
831 + NULL);
832 +
833 + buffer_rrdf_table_add_field(wb, field_id++, "DropsOut", "Dropped Outbound Packets",
834 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
835 + 2, "Drops", max_drops_tx, RRDF_FIELD_SORT_DESCENDING, NULL,
836 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
837 + RRDF_FIELD_OPTS_VISIBLE,
838 + NULL);
839 + }
840 +
841 + buffer_json_object_close(wb); // columns
842 + buffer_json_member_add_string(wb, "default_sort_column", "Total");
843 +
844 + buffer_json_member_add_object(wb, "charts");
845 + {
846 + buffer_json_member_add_object(wb, "Traffic");
847 + {
848 + buffer_json_member_add_string(wb, "name", "Traffic");
849 + buffer_json_member_add_string(wb, "type", "stacked-bar");
850 + buffer_json_member_add_array(wb, "columns");
851 + {
852 + buffer_json_add_array_item_string(wb, "In");
853 + buffer_json_add_array_item_string(wb, "Out");
854 + }
855 + buffer_json_array_close(wb);
856 + }
857 + buffer_json_object_close(wb);
858 +
859 + buffer_json_member_add_object(wb, "Packets");
860 + {
861 + buffer_json_member_add_string(wb, "name", "Packets");
862 + buffer_json_member_add_string(wb, "type", "stacked-bar");
863 + buffer_json_member_add_array(wb, "columns");
864 + {
865 + buffer_json_add_array_item_string(wb, "PktsIn");
866 + buffer_json_add_array_item_string(wb, "PktsOut");
867 + }
868 + buffer_json_array_close(wb);
869 + }
870 + buffer_json_object_close(wb);
871 + }
872 + buffer_json_object_close(wb); // charts
873 +
874 + buffer_json_member_add_array(wb, "default_charts");
875 + {
876 + buffer_json_add_array_item_array(wb);
877 + buffer_json_add_array_item_string(wb, "Traffic");
878 + buffer_json_add_array_item_string(wb, "Name");
879 + buffer_json_array_close(wb);
880 +
881 + buffer_json_add_array_item_array(wb);
882 + buffer_json_add_array_item_string(wb, "Traffic");
883 + buffer_json_add_array_item_string(wb, "Type");
884 + buffer_json_array_close(wb);
885 + }
886 + buffer_json_array_close(wb);
887 +
888 + buffer_json_member_add_time_t(wb, "expires", now_realtime_sec() + 1);
889 + buffer_json_finalize(wb);
890 +
891 + int response = HTTP_RESP_OK;
892 + if(is_cancelled_cb && is_cancelled_cb(is_cancelled_cb_data)) {
893 + buffer_flush(wb);
894 + response = HTTP_RESP_CLIENT_CLOSED_REQUEST;
895 + }
896 +
897 + if(result_cb)
898 + result_cb(wb, response, result_cb_data);
899 +
900 + return response;
901 +}
902 +
903 // netdev data collection
904
905 static void netdev_cleanup() {
@@ -633,6 +961,7 @@ static struct netdev *get_netdev(const char *name) {
961 d->hash = simple_hash(d->name);
962 d->len = strlen(d->name);
963 d->chart_labels = rrdlabels_create();
964 + d->function_ready = false;
965
966 d->chart_type_net_bytes = strdupz("net");
967 d->chart_type_net_compressed = strdupz("net_compressed");
@@ -1485,6 +1814,8 @@ int do_proc_net_dev(int update_every, usec_t dt) {
1814 rrddim_set_by_pointer(d->st_events, d->rd_tcarrier, (collected_number)d->tcarrier);
1815 rrdset_done(d->st_events);
1816 }
1817 +
1818 + d->function_ready = true;
1819 }
1820
1821 if(do_bandwidth == CONFIG_BOOLEAN_YES || (do_bandwidth == CONFIG_BOOLEAN_AUTO &&
@@ -1541,6 +1872,9 @@ void *netdev_main(void *ptr)
1872
1873 netdata_thread_cleanup_push(netdev_main_cleanup, ptr);
1874
1875 + rrd_collector_started();
1876 + rrd_function_add(localhost, NULL, "network-interfaces", 10, RRDFUNCTIONS_NETDEV_HELP, true, netdev_function_net_interfaces, NULL);
1877 +
1878 usec_t step = localhost->rrd_update_every * USEC_PER_SEC;
1879 heartbeat_t hb;
1880 heartbeat_init(&hb);
@@ -1555,10 +1889,14 @@ void *netdev_main(void *ptr)
1889 cgroup_netdev_reset_all();
1890
1891 worker_is_busy(0);
1892 +
1893 + netdata_mutex_lock(&netdev_dev_mutex);
1894 if(do_proc_net_dev(localhost->rrd_update_every, hb_dt))
1895 break;
1896 + netdata_mutex_unlock(&netdev_dev_mutex);
1897 }
1898
1899 netdata_thread_cleanup_pop(1);
1900 +
1901 return NULL;
1902 }