Merge pull request #135 from stoyansbg/master
Added inventory page along with a navbar link.
This commit is contained in:
@@ -180,6 +180,64 @@ def nodes():
|
|||||||
stream_template('nodes.html', nodes=nodes)))
|
stream_template('nodes.html', nodes=nodes)))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/inventory')
|
||||||
|
def inventory():
|
||||||
|
"""Fetch all (active) nodes from PuppetDB and stream a table displaying
|
||||||
|
those nodes along with a set of facts about them.
|
||||||
|
|
||||||
|
Downside of the streaming aproach is that since we've already sent our
|
||||||
|
headers we can't abort the request if we detect an error. Because of this
|
||||||
|
we'll end up with an empty table instead because of how yield_or_stop
|
||||||
|
works. Once pagination is in place we can change this but we'll need to
|
||||||
|
provide a search feature instead.
|
||||||
|
"""
|
||||||
|
|
||||||
|
fact_desc = [] # a list of fact descriptions to go
|
||||||
|
# in the table header
|
||||||
|
fact_names = [] # a list of inventory fact names
|
||||||
|
factvalues = {} # values of the facts for all the nodes
|
||||||
|
# indexed by node name and fact name
|
||||||
|
nodedata = {} # a dictionary containing list of inventoried
|
||||||
|
# facts indexed by node name
|
||||||
|
nodelist = set() # a set of node names
|
||||||
|
|
||||||
|
# get all the facts from PuppetDB
|
||||||
|
facts = puppetdb.facts()
|
||||||
|
|
||||||
|
# load the list of items/facts we want in our inventory
|
||||||
|
try:
|
||||||
|
inv_facts = app.config['INVENTORY_FACTS']
|
||||||
|
except KeyError:
|
||||||
|
inv_facts = [ ('Hostname' ,'fqdn' ),
|
||||||
|
('IP Address' ,'ipaddress' ),
|
||||||
|
('OS' ,'lsbdistdescription'),
|
||||||
|
('Architecture' ,'hardwaremodel' ),
|
||||||
|
('Kernel Version','kernelrelease' ) ]
|
||||||
|
|
||||||
|
# generate a list of descriptions and a list of fact names
|
||||||
|
# from the list of tuples inv_facts.
|
||||||
|
for description,name in inv_facts:
|
||||||
|
fact_desc.append(description)
|
||||||
|
fact_names.append(name)
|
||||||
|
|
||||||
|
# convert the json in easy to access data structure
|
||||||
|
for fact in facts:
|
||||||
|
factvalues[fact.node,fact.name] = fact.value
|
||||||
|
nodelist.add(fact.node)
|
||||||
|
|
||||||
|
# generate the per-host data
|
||||||
|
for node in nodelist:
|
||||||
|
nodedata[node] = []
|
||||||
|
for fact_name in fact_names:
|
||||||
|
try:
|
||||||
|
nodedata[node].append(factvalues[node,fact_name])
|
||||||
|
except KeyError:
|
||||||
|
nodedata[node].append("undef")
|
||||||
|
|
||||||
|
return Response(stream_with_context(
|
||||||
|
stream_template('inventory.html', nodedata=nodedata, fact_desc=fact_desc)))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/node/<node_name>')
|
@app.route('/node/<node_name>')
|
||||||
def node(node_name):
|
def node(node_name):
|
||||||
"""Display a dashboard for a node showing as much data as we have on that
|
"""Display a dashboard for a node showing as much data as we have on that
|
||||||
|
|||||||
@@ -27,3 +27,9 @@ GRAPH_FACTS = ['architecture',
|
|||||||
'osfamily',
|
'osfamily',
|
||||||
'puppetversion',
|
'puppetversion',
|
||||||
'processorcount']
|
'processorcount']
|
||||||
|
INVENTORY_FACTS = [ ('Hostname', 'fqdn' ),
|
||||||
|
('IP Address', 'ipaddress' ),
|
||||||
|
('OS', 'lsbdistdescription'),
|
||||||
|
('Architecture', 'hardwaremodel' ),
|
||||||
|
('Kernel Version', 'kernelrelease' ),
|
||||||
|
('Puppet Version', 'puppetversion' ), ]
|
||||||
|
|||||||
@@ -32,6 +32,10 @@
|
|||||||
sortList: [[1, 0]]
|
sortList: [[1, 0]]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('.inventory').tablesorter({
|
||||||
|
sortList: [[0, 0]]
|
||||||
|
});
|
||||||
|
|
||||||
$('.facts').tablesorter({
|
$('.facts').tablesorter({
|
||||||
sortList: [[0, 0]]
|
sortList: [[0, 0]]
|
||||||
});
|
});
|
||||||
|
|||||||
25
puppetboard/templates/inventory.html
Normal file
25
puppetboard/templates/inventory.html
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{% extends 'layout.html' %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="ui fluid icon input hide" style="margin-bottom:20px">
|
||||||
|
<input autofocus="autofocus" class="filter-table" placeholder="Type here to filter...">
|
||||||
|
</div>
|
||||||
|
<table class='ui compact basic table inventory'>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{% for description in fact_desc %}
|
||||||
|
<th>{{description}}</th>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="searchable">
|
||||||
|
{% for nodename in nodedata %}
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
{% for item in nodedata[nodename] %}
|
||||||
|
<td>{{item}}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endblock content %}
|
||||||
@@ -33,6 +33,7 @@
|
|||||||
('facts', 'Facts'),
|
('facts', 'Facts'),
|
||||||
('reports', 'Reports'),
|
('reports', 'Reports'),
|
||||||
('metrics', 'Metrics'),
|
('metrics', 'Metrics'),
|
||||||
|
('inventory', 'Inventory'),
|
||||||
('query', 'Query'),
|
('query', 'Query'),
|
||||||
] %}
|
] %}
|
||||||
<a {% if endpoint == request.endpoint %} class="active item" {% else %} class="item" {% endif %}
|
<a {% if endpoint == request.endpoint %} class="active item" {% else %} class="item" {% endif %}
|
||||||
|
|||||||
Reference in New Issue
Block a user