Page MenuHomeFreeBSD

usr.sbin/ngctl: Generate more compact GraphWiz output
ClosedPublic

Authored by lutz_donnerhacke.de on Oct 10 2019, 12:21 PM.

Details

Summary

The output of "ngctl dot" is suitable for small netgraph networks. Even moderate complex netgraph setups (about a dozen nodes) are hard to understand from the .dot output, because each node and each hook are shown as a full blown structure.

This patch allows to generate much more compact output and graphs by omitting the extra structures for the individual hooks. Instead the names of the hooks are labels to the edges.

Test Plan

Generate a moderatly complex netgraph.

$ ngctl dot
graph netgraph {
	edge [ weight = 1.0 ];
	node [ shape = record, fontsize = 12 ] {
		"2" [ label = "{hsi1:|{eiface|[2]:}}" ];
		"3" [ label = "{cpe1:|{vlan|[3]:}}" ];
		"4" [ label = "{bng2:|{vlan|[4]:}}" ];
		"5" [ label = "{\<unnamed\>:|{vlan|[5]:}}" ];
		"6" [ label = "{xc:|{tee|[6]:}}" ];
		"7" [ label = "{\<unnamed\>:|{vlan|[7]:}}" ];
		"8" [ label = "{rotate:|{vlan_rotate|[8]:}}" ];
		"9" [ label = "{\<unnamed\>:|{vlan|[9]:}}" ];
		"a" [ label = "{ngeth1:|{eiface|[a]:}}" ];
		"b" [ label = "{tester:|{source|[b]:}}" ];
		"f" [ label = "{\<unnamed\>:|{bridge|[f]:}}" ];
		"10" [ label = "{\<unnamed\>:|{bridge|[10]:}}" ];
		"11" [ label = "{ngeth2:|{eiface|[11]:}}" ];
		"12" [ label = "{ngeth3:|{eiface|[12]:}}" ];
	};
	node [ shape = octagon, fontsize = 10 ] {
		"2.ether" [ label = "ether" ];
	};
	{
		edge [ weight = 2.0, style = bold ];
		"2" -- "2.ether";
	};
	node [ shape = octagon, fontsize = 10 ] {
		"3.downstream" [ label = "downstream" ];
		"3.hsi" [ label = "hsi" ];
	};
	{
		edge [ weight = 2.0, style = bold ];
		"3" -- "3.downstream";
		"3" -- "3.hsi";
	};
	"3.hsi" -- "2.ether";
	node [ shape = octagon, fontsize = 10 ] {
		"4.downstream" [ label = "downstream" ];
		"4.line1" [ label = "line1" ];
	};
	{
		edge [ weight = 2.0, style = bold ];
		"4" -- "4.downstream";
		"4" -- "4.line1";
	};
	"4.line1" -- "3.downstream";
	node [ shape = octagon, fontsize = 10 ] {
		"5.downstream" [ label = "downstream" ];
		"5.xc1" [ label = "xc1" ];
	};
	{
		edge [ weight = 2.0, style = bold ];
		"5" -- "5.downstream";
		"5" -- "5.xc1";
	};
	"5.xc1" -- "4.downstream";
	node [ shape = octagon, fontsize = 10 ] {
		"6.right2left" [ label = "right2left" ];
		"6.left2right" [ label = "left2right" ];
		"6.right" [ label = "right" ];
		"6.left" [ label = "left" ];
	};
	{
		edge [ weight = 2.0, style = bold ];
		"6" -- "6.right2left";
		"6" -- "6.left2right";
		"6" -- "6.right";
		"6" -- "6.left";
	};
	"6.left" -- "5.downstream";
	node [ shape = octagon, fontsize = 10 ] {
		"7.bng2" [ label = "bng2" ];
		"7.downstream" [ label = "downstream" ];
	};
	{
		edge [ weight = 2.0, style = bold ];
		"7" -- "7.bng2";
		"7" -- "7.downstream";
	};
	"7.downstream" -- "6.right";
	node [ shape = octagon, fontsize = 10 ] {
		"8.ordered" [ label = "ordered" ];
		"8.original" [ label = "original" ];
	};
	{
		edge [ weight = 2.0, style = bold ];
		"8" -- "8.ordered";
		"8" -- "8.original";
	};
	"8.original" -- "7.bng2";
	node [ shape = octagon, fontsize = 10 ] {
		"9.hsi" [ label = "hsi" ];
		"9.downstream" [ label = "downstream" ];
	};
	{
		edge [ weight = 2.0, style = bold ];
		"9" -- "9.hsi";
		"9" -- "9.downstream";
	};
	"9.downstream" -- "8.ordered";
	node [ shape = octagon, fontsize = 10 ] {
		"a.ether" [ label = "ether" ];
	};
	{
		edge [ weight = 2.0, style = bold ];
		"a" -- "a.ether";
	};
	"a.ether" -- "9.hsi";
	node [ shape = octagon, fontsize = 10 ] {
		"b.output" [ label = "output" ];
		"b.input" [ label = "input" ];
	};
	{
		edge [ weight = 2.0, style = bold ];
		"b" -- "b.output";
		"b" -- "b.input";
	};
	"b.output" -- "6.right2left";
	"b.input" -- "6.left2right";
	node [ shape = octagon, fontsize = 10 ] {
		"f.link2" [ label = "link2" ];
		"f.link1" [ label = "link1" ];
		"f.link123" [ label = "link123" ];
	};
	{
		edge [ weight = 2.0, style = bold ];
		"f" -- "f.link2";
		"f" -- "f.link1";
		"f" -- "f.link123";
	};
	node [ shape = octagon, fontsize = 10 ] {
		"10.link2" [ label = "link2" ];
		"10.link1" [ label = "link1" ];
		"10.link123" [ label = "link123" ];
	};
	{
		edge [ weight = 2.0, style = bold ];
		"10" -- "10.link2";
		"10" -- "10.link1";
		"10" -- "10.link123";
	};
	"10.link2" -- "f.link2";
	"10.link1" -- "f.link1";
	node [ shape = octagon, fontsize = 10 ] {
		"11.ether" [ label = "ether" ];
	};
	{
		edge [ weight = 2.0, style = bold ];
		"11" -- "11.ether";
	};
	"11.ether" -- "f.link123";
	node [ shape = octagon, fontsize = 10 ] {
		"12.ether" [ label = "ether" ];
	};
	{
		edge [ weight = 2.0, style = bold ];
		"12" -- "12.ether";
	};
	"12.ether" -- "10.link123";
}

And now the compact output:

$ ngctl dot -c
digraph netgraph {
	edge [ dir = "none", fontsize = 10 ];
	node [ shape = record, fontsize = 12 ] {
		"2" [ label = "{hsi1:|{eiface|[2]:}}" ];
		"3" [ label = "{cpe1:|{vlan|[3]:}}" ];
		"4" [ label = "{bng2:|{vlan|[4]:}}" ];
		"5" [ label = "{\<unnamed\>:|{vlan|[5]:}}" ];
		"6" [ label = "{xc:|{tee|[6]:}}" ];
		"7" [ label = "{\<unnamed\>:|{vlan|[7]:}}" ];
		"8" [ label = "{rotate:|{vlan_rotate|[8]:}}" ];
		"9" [ label = "{\<unnamed\>:|{vlan|[9]:}}" ];
		"a" [ label = "{ngeth1:|{eiface|[a]:}}" ];
		"b" [ label = "{tester:|{source|[b]:}}" ];
		"f" [ label = "{\<unnamed\>:|{bridge|[f]:}}" ];
		"10" [ label = "{\<unnamed\>:|{bridge|[10]:}}" ];
		"11" [ label = "{ngeth2:|{eiface|[11]:}}" ];
		"12" [ label = "{ngeth3:|{eiface|[12]:}}" ];
	};
	"2" -> "3" [ headlabel = "hsi", taillabel ="ether" ] ;
	"3" -> "4" [ headlabel = "line1", taillabel ="downstream" ] ;
	"4" -> "5" [ headlabel = "xc1", taillabel ="downstream" ] ;
	"5" -> "6" [ headlabel = "left", taillabel ="downstream" ] ;
	"6" -> "7" [ headlabel = "downstream", taillabel ="right" ] ;
	"7" -> "8" [ headlabel = "original", taillabel ="bng2" ] ;
	"8" -> "9" [ headlabel = "downstream", taillabel ="ordered" ] ;
	"9" -> "a" [ headlabel = "ether", taillabel ="hsi" ] ;
	"6" -> "b" [ headlabel = "output", taillabel ="right2left" ] ;
	"6" -> "b" [ headlabel = "input", taillabel ="left2right" ] ;
	"f" -> "10" [ headlabel = "link2", taillabel ="link2" ] ;
	"f" -> "10" [ headlabel = "link1", taillabel ="link1" ] ;
	"f" -> "11" [ headlabel = "ether", taillabel ="link123" ] ;
	"10" -> "12" [ headlabel = "ether", taillabel ="link123" ] ;
}

Help text ist also available:

$ ngctl help dot
 usage:    dot [-c] [outputfile]
 Aliases:  graphviz, confdot
 Summary:  Produce a GraphViz (.dot) of the entire netgraph.
 Description:
  If no outputfile is specified, stdout will be assumed. The 
  optional -c argument generates a graph without separate 
  structures for egde names. Such a graph is more compact.

You may display such graphs using http://www.webgraphviz.com/

Diff Detail

Repository
rS FreeBSD src repository
Lint
Automatic diff as part of commit; lint not applicable.
Unit
Automatic diff as part of commit; unit tests not applicable.

Event Timeline

I'm afraid I don't know anything about this code.

Change location of working directory locally at my machine.

I tested this patch in our test lab with a lot of netgraph nodes. And I find it very useful.

usr.sbin/ngctl/dot.c
119 ↗(On Diff #63446)

if<space>(

174 ↗(On Diff #63446)

Ditto

195 ↗(On Diff #63446)

Ditto

Fixed spacing for "if (" statements.
Running the whole source through indent(1) would make a much larger patch.

Aside from the minor style nits pointed out, I see nothing wrong with the new additions. ngctl dot -c looks a lot cleaner even for my no-doubt much more trivial setup.

usr.sbin/ngctl/dot.c
177 ↗(On Diff #77546)

Indentation around these needs reassessed; fprintf should be tabbed over from the level of the line above rather than the current two spaces, then the following lines; ditto for the next loop+fprintf construct.

181 ↗(On Diff #77546)

This line should be cleared

In the example compact output, the edge labels overlap and are hard to read, at least with default layout settings using dot(1) or webgraphviz. Is it possible to fix it easily?

usr.sbin/ngctl/dot.c
61 ↗(On Diff #77546)

Typo, "edge"

179 ↗(On Diff #77546)

Same note about indentation as below, it looks wrong here.

196 ↗(On Diff #77546)

Missing a space after the second "="

206 ↗(On Diff #77546)

The indentation of the continuing lines is wrong, it should be by four spaces per style(9).

In the example compact output, the edge labels overlap and are hard to read, at least with default layout settings using dot(1) or webgraphviz. Is it possible to fix it easily?

Unfortunately not. The dot-language does not give control of the location of the labels. This might be the reason for the original design choice, which qualifies the label to separate nodes in the graph.

lutz_donnerhacke.de marked an inline comment as done.

Seems reasonable to me.

This revision is now accepted and ready to land.Sep 29 2020, 9:46 PM

I will likely commit this this weekend if no one objects or does it before that.

This revision was automatically updated to reflect the committed changes.