Merge pull request #72 from ifupdown-ng/feature/depgraph-rdepends
dependency graph: reverse-dependency coloring
This commit is contained in:
		
						commit
						e15f3ffbaa
					
				
					 9 changed files with 156 additions and 11 deletions
				
			
		| 
						 | 
					@ -67,9 +67,9 @@ print_interface_dot(struct lif_dict *collection, struct lif_interface *iface, st
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (parent != NULL)
 | 
						if (parent != NULL)
 | 
				
			||||||
		printf("\"%s\" -> ", parent->ifname);
 | 
							printf("\"%s (%zu)\" -> ", parent->ifname, parent->rdepends_count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	printf("\"%s\"", iface->ifname);
 | 
						printf("\"%s (%zu)\"", iface->ifname, iface->rdepends_count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	printf("\n");
 | 
						printf("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -290,6 +290,12 @@ ifquery_main(int argc, char *argv[])
 | 
				
			||||||
		return EXIT_FAILURE;
 | 
							return EXIT_FAILURE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (lif_lifecycle_count_rdepends(&exec_opts, &collection) == -1)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fprintf(stderr, "%s: could not validate dependency tree\n", argv0);
 | 
				
			||||||
 | 
							return EXIT_FAILURE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* --list --state is not allowed */
 | 
						/* --list --state is not allowed */
 | 
				
			||||||
	if (listing && listing_stat)
 | 
						if (listing && listing_stat)
 | 
				
			||||||
		generic_usage(self_applet, EXIT_FAILURE);
 | 
							generic_usage(self_applet, EXIT_FAILURE);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -101,14 +101,16 @@ skip_interface(struct lif_interface *iface, const char *ifname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (up && iface->refcount > 0)
 | 
						if (up && iface->refcount > 0)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		fprintf(stderr, "%s: skipping %s (already configured), use --force to force configuration\n",
 | 
							if (exec_opts.verbose)
 | 
				
			||||||
 | 
								fprintf(stderr, "%s: skipping auto interface %s (already configured), use --force to force configuration\n",
 | 
				
			||||||
				argv0, ifname);
 | 
									argv0, ifname);
 | 
				
			||||||
		return true;
 | 
							return true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!up && iface->refcount == 0)
 | 
						if (!up && iface->refcount == 0)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		fprintf(stderr, "%s: skipping %s (already deconfigured), use --force to force deconfiguration\n",
 | 
							if (exec_opts.verbose)
 | 
				
			||||||
 | 
								fprintf(stderr, "%s: skipping auto interface %s (already deconfigured), use --force to force deconfiguration\n",
 | 
				
			||||||
				argv0, ifname);
 | 
									argv0, ifname);
 | 
				
			||||||
		return true;
 | 
							return true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -229,6 +231,12 @@ ifupdown_main(int argc, char *argv[])
 | 
				
			||||||
		return EXIT_FAILURE;
 | 
							return EXIT_FAILURE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (lif_lifecycle_count_rdepends(&exec_opts, &collection) == -1)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fprintf(stderr, "%s: could not validate dependency tree\n", argv0);
 | 
				
			||||||
 | 
							return EXIT_FAILURE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!lif_state_sync(&state, &collection))
 | 
						if (!lif_state_sync(&state, &collection))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		fprintf(stderr, "%s: could not sync state\n", argv0);
 | 
							fprintf(stderr, "%s: could not sync state\n", argv0);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,7 +53,8 @@ struct lif_interface {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct lif_dict vars;
 | 
						struct lif_dict vars;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	size_t refcount;	/* >= 0 if up, else 0 */
 | 
						size_t refcount;	/* > 0 if up, else 0 */
 | 
				
			||||||
 | 
						size_t rdepends_count;	/* > 0 if any reverse dependency */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define LIF_INTERFACE_COLLECTION_FOREACH(iter, collection) \
 | 
					#define LIF_INTERFACE_COLLECTION_FOREACH(iter, collection) \
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -339,9 +339,6 @@ lif_lifecycle_run(const struct lif_execute_opts *opts, struct lif_interface *ifa
 | 
				
			||||||
	if (lifname == NULL)
 | 
						if (lifname == NULL)
 | 
				
			||||||
		lifname = iface->ifname;
 | 
							lifname = iface->ifname;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!lif_lifecycle_query_dependents(opts, iface, lifname))
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (up)
 | 
						if (up)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		/* when going up, dependents go up first. */
 | 
							/* when going up, dependents go up first. */
 | 
				
			||||||
| 
						 | 
					@ -389,3 +386,102 @@ lif_lifecycle_run(const struct lif_execute_opts *opts, struct lif_interface *ifa
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool
 | 
				
			||||||
 | 
					count_interface_rdepends(const struct lif_execute_opts *opts, struct lif_dict *collection, struct lif_interface *parent, size_t depth)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* query our dependents if we don't have them already */
 | 
				
			||||||
 | 
						if (!lif_lifecycle_query_dependents(opts, parent, parent->ifname))
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* set rdepends_count to depth, dependents will be depth + 1 */
 | 
				
			||||||
 | 
						parent->rdepends_count = depth;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct lif_dict_entry *requires = lif_dict_find(&parent->vars, "requires");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* no dependents, nothing to worry about */
 | 
				
			||||||
 | 
						if (requires == NULL)
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* walk any dependents */
 | 
				
			||||||
 | 
						char require_ifs[4096] = {};
 | 
				
			||||||
 | 
						strlcpy(require_ifs, requires->data, sizeof require_ifs);
 | 
				
			||||||
 | 
						char *bufp = require_ifs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (char *tokenp = lif_next_token(&bufp); *tokenp; tokenp = lif_next_token(&bufp))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							struct lif_interface *iface = lif_interface_collection_find(collection, tokenp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!count_interface_rdepends(opts, collection, iface, depth + 1))
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ssize_t
 | 
				
			||||||
 | 
					lif_lifecycle_count_rdepends(const struct lif_execute_opts *opts, struct lif_dict *collection)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct lif_node *iter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LIF_DICT_FOREACH(iter, collection)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							struct lif_dict_entry *entry = iter->data;
 | 
				
			||||||
 | 
							struct lif_interface *iface = entry->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* start depth at interface's rdepends_count, which will be 0 for the root,
 | 
				
			||||||
 | 
							 * but will be more if additional rdepends are found...
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (!count_interface_rdepends(opts, collection, iface, iface->rdepends_count))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								fprintf(stderr, "ifupdown: dependency graph is broken for interface %s\n", iface->ifname);
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* figure out the max depth */
 | 
				
			||||||
 | 
						size_t maxdepth = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LIF_DICT_FOREACH(iter, collection)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							struct lif_dict_entry *entry = iter->data;
 | 
				
			||||||
 | 
							struct lif_interface *iface = entry->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (iface->rdepends_count > maxdepth)
 | 
				
			||||||
 | 
								maxdepth = iface->rdepends_count;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* move the collection to a temporary list so we can reorder it */
 | 
				
			||||||
 | 
						struct lif_list temp_list = {};
 | 
				
			||||||
 | 
						struct lif_node *iter_next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LIF_LIST_FOREACH_SAFE(iter, iter_next, collection->list.head)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							void *data = iter->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							lif_node_delete(iter, &collection->list);
 | 
				
			||||||
 | 
							memset(iter, 0, sizeof *iter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							lif_node_insert(iter, data, &temp_list);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* walk backwards from maxdepth to 0, readding nodes */
 | 
				
			||||||
 | 
						for (ssize_t curdepth = maxdepth; curdepth > -1; curdepth--)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							LIF_LIST_FOREACH_SAFE(iter, iter_next, temp_list.head)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								struct lif_dict_entry *entry = iter->data;
 | 
				
			||||||
 | 
								struct lif_interface *iface = entry->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if ((ssize_t) iface->rdepends_count != curdepth)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								lif_node_delete(iter, &temp_list);
 | 
				
			||||||
 | 
								memset(iter, 0, sizeof *iter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								lif_node_insert(iter, entry, &collection->list);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return maxdepth;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,6 +22,7 @@
 | 
				
			||||||
extern bool lif_lifecycle_query_dependents(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *lifname);
 | 
					extern bool lif_lifecycle_query_dependents(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *lifname);
 | 
				
			||||||
extern bool lif_lifecycle_run_phase(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *phase, const char *lifname, bool up);
 | 
					extern bool lif_lifecycle_run_phase(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *phase, const char *lifname, bool up);
 | 
				
			||||||
extern bool lif_lifecycle_run(const struct lif_execute_opts *opts, struct lif_interface *iface, struct lif_dict *collection, struct lif_dict *state, const char *lifname, bool up);
 | 
					extern bool lif_lifecycle_run(const struct lif_execute_opts *opts, struct lif_interface *iface, struct lif_dict *collection, struct lif_dict *state, const char *lifname, bool up);
 | 
				
			||||||
 | 
					extern ssize_t lif_lifecycle_count_rdepends(const struct lif_execute_opts *opts, struct lif_dict *collection);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								tests/fixtures/teardown-dep-ordering.ifstate
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								tests/fixtures/teardown-dep-ordering.ifstate
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					dummy=dummy 2
 | 
				
			||||||
 | 
					bat=bat 2
 | 
				
			||||||
 | 
					br=br 1
 | 
				
			||||||
							
								
								
									
										13
									
								
								tests/fixtures/teardown-dep-ordering.interfaces
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								tests/fixtures/teardown-dep-ordering.interfaces
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					auto dummy
 | 
				
			||||||
 | 
					iface dummy
 | 
				
			||||||
 | 
						link-type dummy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto bat
 | 
				
			||||||
 | 
					iface bat
 | 
				
			||||||
 | 
						link-type dummy
 | 
				
			||||||
 | 
						requires dummy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto br
 | 
				
			||||||
 | 
					iface br
 | 
				
			||||||
 | 
						link-type dummy
 | 
				
			||||||
 | 
						requires bat
 | 
				
			||||||
| 
						 | 
					@ -22,6 +22,7 @@ tests_init \
 | 
				
			||||||
	deferred_teardown_1 \
 | 
						deferred_teardown_1 \
 | 
				
			||||||
	deferred_teardown_2 \
 | 
						deferred_teardown_2 \
 | 
				
			||||||
	deferred_teardown_3 \
 | 
						deferred_teardown_3 \
 | 
				
			||||||
 | 
						teardown_dep_ordering \
 | 
				
			||||||
	regress_opt_f
 | 
						regress_opt_f
 | 
				
			||||||
 | 
					
 | 
				
			||||||
noargs_body() {
 | 
					noargs_body() {
 | 
				
			||||||
| 
						 | 
					@ -172,6 +173,14 @@ deferred_teardown_3_body() {
 | 
				
			||||||
			-i $FIXTURES/deferred-teardown-2.interfaces tun0 tun1 tun2 tun3
 | 
								-i $FIXTURES/deferred-teardown-2.interfaces tun0 tun1 tun2 tun3
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					teardown_dep_ordering_body() {
 | 
				
			||||||
 | 
						atf_check -s exit:0 -o ignore \
 | 
				
			||||||
 | 
							-e match:"skipping auto interface bat" \
 | 
				
			||||||
 | 
							-e match:"skipping auto interface dummy" \
 | 
				
			||||||
 | 
							ifdown -n -i $FIXTURES/teardown-dep-ordering.interfaces \
 | 
				
			||||||
 | 
								-S $FIXTURES/teardown-dep-ordering.ifstate -E $EXECUTORS -a
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
regress_opt_f_body() {
 | 
					regress_opt_f_body() {
 | 
				
			||||||
	atf_check -s exit:0 -o ignore -e ignore \
 | 
						atf_check -s exit:0 -o ignore -e ignore \
 | 
				
			||||||
		ifdown -n -S $FIXTURES/vlan.ifstate -E $EXECUTORS -i $FIXTURES/vlan.interfaces -f eth0.8
 | 
							ifdown -n -S $FIXTURES/vlan.ifstate -E $EXECUTORS -i $FIXTURES/vlan.interfaces -f eth0.8
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,8 @@ tests_init \
 | 
				
			||||||
	learned_dependency \
 | 
						learned_dependency \
 | 
				
			||||||
	learned_dependency_2 \
 | 
						learned_dependency_2 \
 | 
				
			||||||
	learned_executor \
 | 
						learned_executor \
 | 
				
			||||||
	implicit_vlan
 | 
						implicit_vlan \
 | 
				
			||||||
 | 
						teardown_dep_ordering
 | 
				
			||||||
 | 
					
 | 
				
			||||||
noargs_body() {
 | 
					noargs_body() {
 | 
				
			||||||
	atf_check -s exit:1 -e ignore ifup -S/dev/null
 | 
						atf_check -s exit:1 -e ignore ifup -S/dev/null
 | 
				
			||||||
| 
						 | 
					@ -131,3 +132,10 @@ implicit_vlan_body() {
 | 
				
			||||||
		-e match:"attempting to run link executor" \
 | 
							-e match:"attempting to run link executor" \
 | 
				
			||||||
		ifup -n -S/dev/null -E $EXECUTORS -i $FIXTURES/vlan.interfaces eth0.8
 | 
							ifup -n -S/dev/null -E $EXECUTORS -i $FIXTURES/vlan.interfaces eth0.8
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					teardown_dep_ordering_body() {
 | 
				
			||||||
 | 
						atf_check -s exit:0 -o ignore \
 | 
				
			||||||
 | 
							-e match:"skipping auto interface bat" \
 | 
				
			||||||
 | 
							-e match:"skipping auto interface dummy" \
 | 
				
			||||||
 | 
							ifup -n -i $FIXTURES/teardown-dep-ordering.interfaces -E $EXECUTORS -a
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue