generate ascii graph of topology based on inventory

I think it’d be useful/interesting to generate an ascii graph of topology based on the configured inventory. Assuming this doesn’t exist already, if I contributed this feature, what are the chances it’d be accepted?

​I'm not totally sure what you mean by such a graph, given how Ansible
inventory can be multiple "trees"​ of group etc.?

Do you have some POC, or more info on what you want to do, and also, why?

      Serge

Totally, that would be cool.

We’d definitely take it as an examples/ script.

It would probably be best if it generated something in “dot” format and maybe it shells out to graphviz “dot” when done.

GraphViz tends to suck if you have a lot of hosts so an option to maybe print the hosts or not (or just show the counts) might be relevant.

You could use the inventory API to surf this and should be able to work with dynamic inventory scripts as well.

–Michael

I’m not totally sure what you mean by such a graph, given how Ansible inventory can be multiple “trees” of group etc.?

You’re right, it would be a set of trees.

Rough example I made on asciiflow.com:

[zookeepers]

Should I PR against https://github.com/ansible/ansible-examples?

Nevermind, I see https://github.com/ansible/ansible/tree/devel/examples/scripts

Actually it can be a graph and is not just a series of trees, which is why we actually nixed the YAML inventory plugin a while back, because YAML is treeish :slight_smile:

Two groups can have the same kid, etc!

​I once started on that idea​, but didn't get through to it. I got as far
as to this script:

     https://github.com/ginsys/ansible-plugins/blob/devel/bin/ansible-vars

more specifically the output of

    ansible-vars --list-groups

I definitely look forward to your ideas!

   Serge

Idea - http://blockdiag.com/en/nwdiag/index.html - no graphviz needed.

Is there and export of hosts/groups in some graph format already?
I have some viz is JavaScript:
http://techtonik.bitbucket.org/graphs/index.html

and for simple graphs it is possible to support parsing a CSV of edges.

​No, the way Inventory objects are modelled, you just have hosts and a list
groups they are member of​, but the parent child relation between groups is
not kept.
You''ll have to dig in the Inventory code to understand how to extract this.

Is that it?

group2 <---- host ---> group1

What other kind of parent-child relationship order is needed?

​I'm not sure what you mean here?

A host can be member of a group; a group can be member of another group,
etc.​

So, information about subgroups is not available, right?
http://docs.ansible.com/intro_inventory.html#groups-of-groups-and-group-variables

So this is not parsed:

[atlanta]
host1
host2

[raleigh]
host2
host3

[southeast:children]
atlanta
raleigh

And it is impossible to build:

host3 ---> raleigh ---> southeast <--- atlanta <--- host1
                               <---- host2------>

Unless I’m mistaken, which I shouldn’t be as I wrote the code, this isn’t actually the case.

https://github.com/ansible/ansible/blob/devel/lib/ansible/inventory/group.py

There is “parent_group” information kept on every group object, which is also what “get_ancestors” returns.

It’s available and quite possible.

B​ut, keep in mind that 'parent_group' is a flat list of *all* ancestors,
not only the direct parent above of which a group is a child​, which would
make it hard for the op to reconstruct the full tree relation of groups.

For the OP's example, for

               parents = group.get_ancestors()

               print '%s (depth %s): parents: %s' % (group.name,
group.depth, [ '%s (depth %s)' % (x.name, x.depth) for x in parents ])

you get this

     all (depth 0): parents:
     atlanta (depth 2): parents: ['southeast (depth 1)', 'all (depth 0)']
     raleigh (depth 2): parents: ['southeast (depth 1)', 'all (depth 0)']
     southeast (depth 1): parents: ['all (depth 0)']

​So you have to take into account the depth value of each group.
Also, for larger, complex trees, sourced from different inventory files or
scripts spread over different directories, this depth value is sometimes
wrong.

    Serge​

Curious. A bit of an artifact of the needs of the system to just feed input into Ansible, for sure.

Seems like it would be easier to just should fix the glitch in the object so the group object remembers it’s direct parents in that case.

That way everybody can make use of it.

​That's a very big +1 from me.

  Serge​

Somebody send in a patch :slight_smile:

I dug into the code, and got some feedback for you

I dug into the code, and got some feedback for you

There is "parent_group" information kept on every group object, which is
also what "get_ancestors" returns.

Actually,
you are right that
parent_group contains the direct parents,
but
get_ancestors will also return all grandparents.

This part seems good.

Except when using the dir.py, where parent_group is filled from
group.get_ancestors instead of group.parent_groups
, which is a bug there

Got a patch for that in
my
queue.

Excellent!

But, keep in mind that 'parent_group' is a flat list of *all*

ancestors, not only the direct parent above of which a group is a child,
which would make it hard for the op to reconstruct the full tree relation
of groups.

So this remark is actually based on a bug when using a directory as the
inventory. Easy fix.

Anothe
r
thing I noticed, is that ini.py and script.py both will add every single
group - regardless of it's location in the 'tree' - to the default 'all'
group, which is also shown in my previous example, where the all group is
listed in parent_groups of every group:

     all (depth 0): parents:

     atlanta (depth 2): parents: ['southeast (de

pth 1)', 'all (depth 0)']
     raleigh (depth 2): parents: ['southeast (depth 1)', 'all (depth 0)']
     southeast (depth 1): parents: ['all (depth 0)']

How I t
h
ink of it, is actually only groups with depth == 1 should be (direct)
child's of the all group. Resolving all groups and hosts for a group
and host
is done elsewhere.

yeah that seems suboptimal and weird in the code, but also not causing of
damage. Fine if we fix as it will make classes displaying the inventory
more sensical (as this was trying to do about the graph)

I
made
a patch to update that in the ini code, still need to do that for the
script code.

And whilst at this, I had a look at the calculation of the depth
parameter, and improved it by updating it for every grandchild, when
updating a new child group.

I also noticed groups and hosts sometimes get duplicated in certain object
lists, and added some checks for that too.

From the op's example, I now get a much cleaner output:

serge@goldorak:~$ ansible-vars -i h --list-groups --list-hosts
(0) all . . . . . . . . . . . . . . . . . . . . . .( parents: )
+ (1) ungrouped . . . . . . . . . . . . . . . . . .( parents: all(0) )
+ (1) southeast . . . . . . . . . . . . . . . . . .( parents: all(0) )
++ (2) atlanta . . . . . . . . . . . . . . . . . .( parents: southeast(1) )
        \_________________________________ [ host1,host2 ]
++ (2) raleigh . . . . . . . . . . . . . . . . . .( parents: southeast(1) )
        \_________________________________ [ host2,host3 ]

(whereas previously you would also see hosts in eg the southeast group)
From here it will be far simpler to do some kind of tree-ish output.

Yay! (Yep, it is technically a graph, FWIW... but tree-ish would be
appropriate for something that is not quite a tree :slight_smile: )

Still need to do dome more tests, and check the scripts behaviour.
Will do a PR later.

If someone's sees a problem so far, please y
ell :slight_smile: Especially I'd like feedback about the option to (not) put
*every* group as a direct child of the parent.

We should fix this and I don't think it will hurt anything, as long as the
tests for inventory still pass and all that good stuff.

In the mean time I'm happy I got this tree as I always wanted it.

Happy Little Trees!

(Not sure if you get Bob Ross reruns in Europe)