@cryptotaxi247 / netdata-1 / commits / 19a3ac9e0

basic functionality for renaming network interfaces

Costa Tsaousis (ktsaou) committed Jul 31, 2017 at 00:46 UTC 19a3ac9e03c46986f3fa487e8fb2ee9ac61aafce
5 files changed +315 -52
src/cgroup-network.c
+41 -3
@@ -176,6 +176,9 @@ int switch_namespace(const char *prefix, pid_t pid) {
176 // 2 passes - found it at nsenter source code
177 // this is related CLONE_NEWUSER functionality
178
179 + // FIXME: this code cannot switch user namespace
180 + // Fortunately, we don't need it.
181 +
182 int pass, errors = 0;
183 for(pass = 0; pass < 2 ;pass++) {
184 for(i = 0; all_ns[i].name ; i++) {
@@ -237,8 +240,35 @@ int switch_namespace(const char *prefix, pid_t pid) {
240 #endif
241 }
242
243 +pid_t read_pid_from_cgroup(const char *path) {
244 + char buffer[FILENAME_MAX + 1];
245 +
246 + snprintfz(buffer, FILENAME_MAX, "%s/cgroup.procs", path);
247 + FILE *fp = fopen(buffer, "r");
248 + if(!fp) {
249 + error("Cannot read file '%s'.", buffer);
250 + snprintfz(buffer, FILENAME_MAX, "%s/tasks", path);
251 + fp = fopen(buffer, "r");
252 + }
253 +
254 + if(!fp) {
255 + error("Cannot read file '%s'.", buffer);
256 + return 0;
257 + }
258 +
259 + pid_t pid = 0;
260 + char *s;
261 + while((s = fgets(buffer, FILENAME_MAX, fp))) {
262 + pid = atoi(s);
263 + if(pid > 0) break;
264 + }
265 +
266 + fclose(fp);
267 + return pid;
268 +}
269 +
270 void usage(void) {
241 - fprintf(stderr, "%s -p PID\n", program_name);
271 + fprintf(stderr, "%s [ -p PID | --pid PID | --cgroup /path/to/cgroup ]\n", program_name);
272 exit(1);
273 }
274
@@ -254,10 +284,18 @@ int main(int argc, char **argv) {
284 exit(0);
285 }
286
257 - if(argc != 3 || strcmp(argv[1], "-p") != 0)
287 + if(argc != 3)
288 + usage();
289 +
290 + if(!strcmp(argv[1], "-p") || !strcmp(argv[1], "--pid")) {
291 + pid = atoi(argv[2]);
292 + }
293 + else if(!strcmp(argv[1], "--cgroup")) {
294 + pid = read_pid_from_cgroup(argv[2]);
295 + }
296 + else
297 usage();
298
260 - pid = atoi(argv[2]);
299 if(pid <= 0)
300 fatal("Invalid pid %d", (int)pid);
301
src/freebsd_getifaddrs.c
+11 -11
@@ -2,7 +2,7 @@
2
3 #include <ifaddrs.h>
4
5 -struct network_interface {
5 +struct cgroup_network_interface {
6 char *name;
7 uint32_t hash;
8 size_t len;
@@ -41,14 +41,14 @@ struct network_interface {
41 RRDSET *st_events;
42 RRDDIM *rd_events_coll;
43
44 - struct network_interface *next;
44 + struct cgroup_network_interface *next;
45 };
46
47 -static struct network_interface *network_interfaces_root = NULL, *network_interfaces_last_used = NULL;
47 +static struct cgroup_network_interface *network_interfaces_root = NULL, *network_interfaces_last_used = NULL;
48
49 static size_t network_interfaces_added = 0, network_interfaces_found = 0;
50
51 -static void network_interface_free(struct network_interface *ifm) {
51 +static void network_interface_free(struct cgroup_network_interface *ifm) {
52 if (likely(ifm->st_bandwidth))
53 rrdset_is_obsolete(ifm->st_bandwidth);
54 if (likely(ifm->st_packets))
@@ -68,7 +68,7 @@ static void network_interface_free(struct network_interface *ifm) {
68 static void network_interfaces_cleanup() {
69 if (likely(network_interfaces_found == network_interfaces_added)) return;
70
71 - struct network_interface *ifm = network_interfaces_root, *last = NULL;
71 + struct cgroup_network_interface *ifm = network_interfaces_root, *last = NULL;
72 while(ifm) {
73 if (unlikely(!ifm->updated)) {
74 // info("Removing network interface '%s', linked after '%s'", ifm->name, last?last->name:"ROOT");
@@ -76,7 +76,7 @@ static void network_interfaces_cleanup() {
76 if (network_interfaces_last_used == ifm)
77 network_interfaces_last_used = last;
78
79 - struct network_interface *t = ifm;
79 + struct cgroup_network_interface *t = ifm;
80
81 if (ifm == network_interfaces_root || !last)
82 network_interfaces_root = ifm = ifm->next;
@@ -95,8 +95,8 @@ static void network_interfaces_cleanup() {
95 }
96 }
97
98 -static struct network_interface *get_network_interface(const char *name) {
99 - struct network_interface *ifm;
98 +static struct cgroup_network_interface *get_network_interface(const char *name) {
99 + struct cgroup_network_interface *ifm;
100
101 uint32_t hash = simple_hash(name);
102
@@ -117,7 +117,7 @@ static struct network_interface *get_network_interface(const char *name) {
117 }
118
119 // create a new one
120 - ifm = callocz(1, sizeof(struct network_interface));
120 + ifm = callocz(1, sizeof(struct cgroup_network_interface));
121 ifm->name = strdupz(name);
122 ifm->hash = simple_hash(ifm->name);
123 ifm->len = strlen(ifm->name);
@@ -125,7 +125,7 @@ static struct network_interface *get_network_interface(const char *name) {
125
126 // link it to the end
127 if (network_interfaces_root) {
128 - struct network_interface *e;
128 + struct cgroup_network_interface *e;
129 for(e = network_interfaces_root; e->next ; e = e->next) ;
130 e->next = ifm;
131 }
@@ -288,7 +288,7 @@ int do_getifaddrs(int update_every, usec_t dt) {
288 if (ifa->ifa_addr->sa_family != AF_LINK)
289 continue;
290
291 - struct network_interface *ifm = get_network_interface(ifa->ifa_name);
291 + struct cgroup_network_interface *ifm = get_network_interface(ifa->ifa_name);
292 ifm->updated = 1;
293 network_interfaces_found++;
294
src/plugin_proc.h
+4
@@ -32,4 +32,8 @@ extern int get_numa_node_count(void);
32 // metrics that need to be shared among data collectors
33 extern unsigned long long tcpext_TCPSynRetrans;
34
35 +// netdev renames
36 +extern void netdev_rename_device_add(const char *host_device, const char *container_device, const char *container_name);
37 +extern void netdev_rename_device_del(const char *host_device);
38 +
39 #endif /* NETDATA_PLUGIN_PROC_H */
src/proc_net_dev.c
+171 -38
@@ -1,5 +1,8 @@
1 #include "common.h"
2
3 +// ----------------------------------------------------------------------------
4 +// netdev list
5 +
6 struct netdev {
7 char *name;
8 uint32_t hash;
@@ -18,6 +21,20 @@ struct netdev {
21 int do_compressed;
22 int do_events;
23
24 + const char *chart_type_net_bytes;
25 + const char *chart_type_net_packets;
26 + const char *chart_type_net_errors;
27 + const char *chart_type_net_fifo;
28 + const char *chart_type_net_events;
29 + const char *chart_type_net_drops;
30 + const char *chart_type_net_compressed;
31 +
32 + const char *chart_family;
33 + const char *chart_name;
34 + const char *chart_id;
35 +
36 + unsigned long priority;
37 +
38 // data collected
39 kernel_uint_t rbytes;
40 kernel_uint_t rpackets;
@@ -68,8 +85,101 @@ struct netdev {
85 struct netdev *next;
86 };
87
71 -static struct netdev *netdev_root = NULL, *netdev_last_used = NULL;
88 +// ----------------------------------------------------------------------------
89 +// netdev renames
90 +
91 +static struct netdev_rename {
92 + const char *host_device;
93 + uint32_t hash;
94 +
95 + const char *container_device;
96 + const char *container_name;
97 +
98 + int processed;
99
100 + struct netdev_rename *next;
101 +} *netdev_rename_root = NULL;
102 +
103 +static int netdev_pending_renames = 0;
104 +static netdata_mutex_t netdev_rename_mutex = NETDATA_MUTEX_INITIALIZER;
105 +
106 +static struct netdev_rename *netdev_rename_find(const char *host_device, uint32_t hash) {
107 + struct netdev_rename *r;
108 +
109 + for(r = netdev_rename_root; r ; r = r->next)
110 + if(r->hash == hash && !strcmp(host_device, r->host_device))
111 + return r;
112 +
113 + return NULL;
114 +}
115 +
116 +// other threads can call this function to register a rename to a netdev
117 +void netdev_rename_device_add(const char *host_device, const char *container_device, const char *container_name) {
118 + netdata_mutex_lock(&netdev_rename_mutex);
119 +
120 + uint32_t hash = simple_hash(host_device);
121 + struct netdev_rename *r = netdev_rename_find(host_device, hash);
122 + if(!r) {
123 + r = callocz(1, sizeof(struct netdev_rename));
124 + r->host_device = strdupz(host_device);
125 + r->container_device = strdupz(container_device);
126 + r->container_name = strdupz(container_name);
127 + r->hash = hash;
128 + r->next = netdev_rename_root;
129 + netdev_rename_root = r;
130 + netdev_pending_renames++;
131 + info("CGROUP: registered network interface rename for '%s' as '%s' under '%s'", r->host_device, r->container_device, r->container_name);
132 + }
133 + else {
134 + if(strcmp(r->container_device, container_device) != 0 || strcmp(r->container_name, container_name) != 0) {
135 + freez((void *) r->container_device);
136 + freez((void *) r->container_name);
137 +
138 + r->container_device = strdupz(container_device);
139 + r->container_name = strdupz(container_name);
140 + r->processed = 0;
141 + netdev_pending_renames++;
142 + info("CGROUP: altered network interface rename for '%s' as '%s' under '%s'", r->host_device, r->container_device, r->container_name);
143 + }
144 + }
145 +
146 + netdata_mutex_unlock(&netdev_rename_mutex);
147 +}
148 +
149 +// other threads can call this function to delete a rename to a netdev
150 +void netdev_rename_device_del(const char *host_device) {
151 + netdata_mutex_lock(&netdev_rename_mutex);
152 +
153 + struct netdev_rename *r, *last = NULL;
154 +
155 + uint32_t hash = simple_hash(host_device);
156 + for(r = netdev_rename_root; r ; last = r, r = r->next) {
157 + if (r->hash == hash && !strcmp(host_device, r->host_device)) {
158 + if (netdev_rename_root == r)
159 + netdev_rename_root = r->next;
160 + else if (last)
161 + last->next = r->next;
162 +
163 + if(!r->processed)
164 + netdev_pending_renames--;
165 +
166 + info("CGROUP: unregistered network interface rename for '%s' as '%s' under '%s'", r->host_device, r->container_device, r->container_name);
167 +
168 + freez((void *) r->host_device);
169 + freez((void *) r->container_name);
170 + freez((void *) r->container_device);
171 + freez((void *) r);
172 + break;
173 + }
174 + }
175 +
176 + netdata_mutex_unlock(&netdev_rename_mutex);
177 +}
178 +
179 +// ----------------------------------------------------------------------------
180 +// netdev data collection
181 +
182 +static struct netdev *netdev_root = NULL, *netdev_last_used = NULL;
183 static size_t netdev_added = 0, netdev_found = 0;
184
185 static void netdev_free(struct netdev *d) {
@@ -82,8 +192,18 @@ static void netdev_free(struct netdev *d) {
192 if(d->st_events) rrdset_is_obsolete(d->st_events);
193
194 netdev_added--;
85 - freez(d->name);
86 - freez(d);
195 + freez((void *)d->chart_type_net_bytes);
196 + freez((void *)d->chart_type_net_compressed);
197 + freez((void *)d->chart_type_net_drops);
198 + freez((void *)d->chart_type_net_errors);
199 + freez((void *)d->chart_type_net_events);
200 + freez((void *)d->chart_type_net_fifo);
201 + freez((void *)d->chart_type_net_packets);
202 + freez((void *)d->chart_family);
203 + freez((void *)d->chart_name);
204 + freez((void *)d->chart_id);
205 + freez((void *)d->name);
206 + freez((void *)d);
207 }
208
209 static void netdev_cleanup() {
@@ -144,6 +264,19 @@ static struct netdev *get_netdev(const char *name) {
264 d->name = strdupz(name);
265 d->hash = simple_hash(d->name);
266 d->len = strlen(d->name);
267 +
268 + d->chart_type_net_bytes = strdupz("net");
269 + d->chart_type_net_compressed = strdupz("net_compressed");
270 + d->chart_type_net_drops = strdupz("net_drops");
271 + d->chart_type_net_errors = strdupz("net_errors");
272 + d->chart_type_net_events = strdupz("net_events");
273 + d->chart_type_net_fifo = strdupz("net_fifo");
274 + d->chart_type_net_packets = strdupz("net_packets");
275 + d->chart_family = strdupz(d->name);
276 + d->chart_name = NULL;
277 + d->chart_id = strdupz(d->name);
278 + d->priority = 7000;
279 +
280 netdev_added++;
281
282 // link it to the end
@@ -278,14 +411,14 @@ int do_proc_net_dev(int update_every, usec_t dt) {
411 if(unlikely(!d->st_bandwidth)) {
412
413 d->st_bandwidth = rrdset_create_localhost(
281 - "net"
282 - , d->name
283 - , NULL
284 - , d->name
414 + d->chart_type_net_bytes
415 + , d->chart_id
416 + , d->chart_name
417 + , d->chart_family
418 , "net.net"
419 , "Bandwidth"
420 , "kilobits/s"
288 - , 7000
421 + , d->priority
422 , update_every
423 , RRDSET_TYPE_AREA
424 );
@@ -309,14 +442,14 @@ int do_proc_net_dev(int update_every, usec_t dt) {
442 if(unlikely(!d->st_packets)) {
443
444 d->st_packets = rrdset_create_localhost(
312 - "net_packets"
313 - , d->name
314 - , NULL
315 - , d->name
445 + d->chart_type_net_packets
446 + , d->chart_id
447 + , d->chart_name
448 + , d->chart_family
449 , "net.packets"
450 , "Packets"
451 , "packets/s"
319 - , 7001
452 + , d->priority + 1
453 , update_every
454 , RRDSET_TYPE_LINE
455 );
@@ -344,14 +477,14 @@ int do_proc_net_dev(int update_every, usec_t dt) {
477 if(unlikely(!d->st_errors)) {
478
479 d->st_errors = rrdset_create_localhost(
347 - "net_errors"
348 - , d->name
349 - , NULL
350 - , d->name
480 + d->chart_type_net_errors
481 + , d->chart_id
482 + , d->chart_name
483 + , d->chart_family
484 , "net.errors"
485 , "Interface Errors"
486 , "errors/s"
354 - , 7002
487 + , d->priority + 2
488 , update_every
489 , RRDSET_TYPE_LINE
490 );
@@ -377,14 +510,14 @@ int do_proc_net_dev(int update_every, usec_t dt) {
510 if(unlikely(!d->st_drops)) {
511
512 d->st_drops = rrdset_create_localhost(
380 - "net_drops"
381 - , d->name
382 - , NULL
383 - , d->name
513 + d->chart_type_net_drops
514 + , d->chart_id
515 + , d->chart_name
516 + , d->chart_family
517 , "net.drops"
518 , "Interface Drops"
519 , "drops/s"
387 - , 7003
520 + , d->priority + 3
521 , update_every
522 , RRDSET_TYPE_LINE
523 );
@@ -410,14 +543,14 @@ int do_proc_net_dev(int update_every, usec_t dt) {
543 if(unlikely(!d->st_fifo)) {
544
545 d->st_fifo = rrdset_create_localhost(
413 - "net_fifo"
414 - , d->name
415 - , NULL
416 - , d->name
546 + d->chart_type_net_fifo
547 + , d->chart_id
548 + , d->chart_name
549 + , d->chart_family
550 , "net.fifo"
551 , "Interface FIFO Buffer Errors"
552 , "errors"
420 - , 7004
553 + , d->priority + 4
554 , update_every
555 , RRDSET_TYPE_LINE
556 );
@@ -443,14 +576,14 @@ int do_proc_net_dev(int update_every, usec_t dt) {
576 if(unlikely(!d->st_compressed)) {
577
578 d->st_compressed = rrdset_create_localhost(
446 - "net_compressed"
447 - , d->name
448 - , NULL
449 - , d->name
579 + d->chart_type_net_compressed
580 + , d->chart_id
581 + , d->chart_name
582 + , d->chart_family
583 , "net.compressed"
584 , "Compressed Packets"
585 , "packets/s"
453 - , 7005
586 + , d->priority + 5
587 , update_every
588 , RRDSET_TYPE_LINE
589 );
@@ -476,14 +609,14 @@ int do_proc_net_dev(int update_every, usec_t dt) {
609 if(unlikely(!d->st_events)) {
610
611 d->st_events = rrdset_create_localhost(
479 - "net_events"
480 - , d->name
481 - , NULL
482 - , d->name
612 + d->chart_type_net_events
613 + , d->chart_id
614 + , d->chart_name
615 + , d->chart_family
616 , "net.events"
617 , "Network Interface Events"
618 , "events/s"
486 - , 7006
619 + , d->priority + 6
620 , update_every
621 , RRDSET_TYPE_LINE
622 );
src/sys_fs_cgroup.c
+88
@@ -50,6 +50,7 @@ static SIMPLE_PATTERN *enabled_cgroup_renames = NULL;
50 static SIMPLE_PATTERN *systemd_services_cgroups = NULL;
51
52 static char *cgroups_rename_script = NULL;
53 +static char *cgroups_network_interface_script = NULL;
54
55 static int cgroups_check = 0;
56
@@ -191,6 +192,9 @@ void read_cgroup_plugin_configuration() {
192 snprintfz(filename, FILENAME_MAX, "%s/cgroup-name.sh", netdata_configured_plugins_dir);
193 cgroups_rename_script = config_get("plugin:cgroups", "script to get cgroup names", filename);
194
195 + snprintfz(filename, FILENAME_MAX, "%s/cgroup-network", netdata_configured_plugins_dir);
196 + cgroups_network_interface_script = config_get("plugin:cgroups", "script to get cgroup network interfaces", filename);
197 +
198 enabled_cgroup_renames = simple_pattern_create(
199 config_get("plugin:cgroups", "run script to rename cgroups matching",
200 " *.scope "
@@ -329,6 +333,12 @@ struct cpuacct_usage {
333 unsigned long long *cpu_percpu;
334 };
335
336 +struct cgroup_network_interface {
337 + const char *host_device;
338 + const char *container_device;
339 + struct cgroup_network_interface *next;
340 +};
341 +
342 #define CGROUP_OPTIONS_DISABLED_DUPLICATE 0x00000001
343 #define CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE 0x00000002
344
@@ -360,6 +370,8 @@ struct cgroup {
370 struct blkio io_merged; // operations
371 struct blkio io_queued; // operations
372
373 + struct cgroup_network_interface *interfaces;
374 +
375 // per cgroup charts
376 RRDSET *st_cpu;
377 RRDSET *st_cpu_per_core;
@@ -717,6 +729,77 @@ static inline void read_all_cgroups(struct cgroup *root) {
729 cgroup_read(cg);
730 }
731
732 +// ----------------------------------------------------------------------------
733 +// cgroup network interfaces
734 +
735 +#define CGROUP_NETWORK_INTERFACE_MAX_LINE 2048
736 +static inline void read_cgroup_network_interfaces(struct cgroup *cg) {
737 + debug(D_CGROUP, "looking for the network interfaces of cgroup '%s' with chart id '%s' and title '%s'", cg->id, cg->chart_id, cg->chart_title);
738 +
739 + pid_t cgroup_pid;
740 + char buffer[CGROUP_NETWORK_INTERFACE_MAX_LINE + 1];
741 +
742 + snprintfz(buffer, CGROUP_NETWORK_INTERFACE_MAX_LINE, "exec %s --cgroup '%s%s'", cgroups_network_interface_script, cgroup_cpuacct_base, cg->id);
743 +
744 + debug(D_CGROUP, "executing command '%s' for cgroup '%s'", buffer, cg->id);
745 + FILE *fp = mypopen(buffer, &cgroup_pid);
746 + if(fp) {
747 + char *s;
748 + while((s = fgets(buffer, CGROUP_NETWORK_INTERFACE_MAX_LINE, fp))) {
749 + trim(s);
750 +
751 + if(*s && *s != '\n') {
752 + char *t = s;
753 + while(*t && *t != ' ') t++;
754 + if(*t == ' ') {
755 + *t = '\0';
756 + t++;
757 + }
758 +
759 + if(!*s) {
760 + error("CGROUP: empty host interface returned by script");
761 + continue;
762 + }
763 +
764 + if(!*t) {
765 + error("CGROUP: empty container interface returned by script");
766 + continue;
767 + }
768 +
769 + struct cgroup_network_interface *i = callocz(1, sizeof(struct cgroup_network_interface));
770 + i->host_device = strdupz(s);
771 + i->container_device = strdupz(t);
772 + i->next = cg->interfaces;
773 + cg->interfaces = i;
774 +
775 + info("CGROUP: cgroup '%s' has network interface '%s' as '%s'", cg->id, i->host_device, i->container_device);
776 +
777 + // register a device rename to proc_net_dev.c
778 + netdev_rename_device_add(i->host_device, i->container_device, cg->chart_id);
779 + }
780 + }
781 +
782 + mypclose(fp, cgroup_pid);
783 + // debug(D_CGROUP, "closed command for cgroup '%s'", cg->id);
784 + }
785 + else
786 + error("CGROUP: cannot popen(\"%s\", \"r\").", buffer);
787 +}
788 +
789 +static inline void free_cgroup_network_interfaces(struct cgroup *cg) {
790 + while(cg->interfaces) {
791 + struct cgroup_network_interface *i = cg->interfaces;
792 + cg->interfaces = i->next;
793 +
794 + // delete the registration of proc_net_dev rename
795 + netdev_rename_device_del(i->host_device);
796 +
797 + freez((void *)i->host_device);
798 + freez((void *)i->container_device);
799 + freez((void *)i);
800 + }
801 +}
802 +
803 // ----------------------------------------------------------------------------
804 // add/remove/find cgroup objects
805
@@ -893,6 +976,9 @@ static inline struct cgroup *cgroup_add(const char *id) {
976 }
977 }
978
979 + if(cg->enabled && !(cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE))
980 + read_cgroup_network_interfaces(cg);
981 +
982 debug(D_CGROUP, "ADDED CGROUP: '%s' with chart id '%s' and title '%s' as %s (default was %s)", cg->id, cg->chart_id, cg->chart_title, (cg->enabled)?"enabled":"disabled", (def)?"enabled":"disabled");
983
984 return cg;
@@ -916,6 +1002,8 @@ static inline void cgroup_free(struct cgroup *cg) {
1002 if(cg->st_queued_ops) rrdset_is_obsolete(cg->st_queued_ops);
1003 if(cg->st_merged_ops) rrdset_is_obsolete(cg->st_merged_ops);
1004
1005 + free_cgroup_network_interfaces(cg);
1006 +
1007 freez(cg->cpuacct_usage.cpu_percpu);
1008
1009 freez(cg->cpuacct_stat.filename);