diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 0000000..d2a7bd4 --- /dev/null +++ b/.bowerrc @@ -0,0 +1,4 @@ +{ + "directory": "www/vendor/", + "analytics": false +} diff --git a/.gitignore b/.gitignore index cb7a60f..fab8eb9 100644 --- a/.gitignore +++ b/.gitignore @@ -60,4 +60,4 @@ target/ # application www/*.html www/hosts/*.html -www/networks/*.html +www/vendor diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..433de40 --- /dev/null +++ b/bower.json @@ -0,0 +1,11 @@ +{ + "name": "saltinventory", + "version": "0.0.0", + "private": true, + "dependencies": { + "jquery": ">=2.1.4", + "jquery-number": ">=2.1.6", + "datatables": ">=1.10.9", + "bootstrap": "~3.3.5" + } +} diff --git a/saltinventory.py b/saltinventory.py index 7471207..118ccc8 100755 --- a/saltinventory.py +++ b/saltinventory.py @@ -4,19 +4,17 @@ # Requirements: # # * salt -# * python-netaddr # import salt.client -from netaddr import * +import salt.runner +import salt.config from jinja2 import Template, BaseLoader, TemplateNotFound, FileSystemLoader, Environment # settings config = { 'base_path': 'www/', -'host_page_path': 'www/hosts/', -'network_page_path': 'www/networks/', 'template_path': 'templates/', } @@ -26,94 +24,35 @@ class Inventorizer: config = None saltcmd = None + saltrun = None host_list = {} - network_list = {} def __init__(self, config): self.config = config + self.saltcmd = salt.client.LocalClient() + + opts = salt.config.master_config('/etc/salt/master') + self.saltrun = salt.runner.RunnerClient(opts) + self.process() def process(self): self.collectHostData() self.createHostList() - self.collectNetworkData() - self.createHostPages() - self.createNetworkList() - self.createNetworkPages() def collectHostData(self): self.host_list = self.saltcmd.cmd('*', 'grains.items') - - def collectNetworkData(self): - result = self.saltcmd.cmd('*', 'network.interfaces') - for hostname, hostdata in result.iteritems(): - for interface, interfacedata in hostdata.iteritems(): - - if interfacedata.has_key('inet'): - for netdata in interfacedata['inet']: - ip = IPNetwork(netdata['address'] + '/' + netdata['netmask']) - host = {'hostname': hostname, 'address': netdata['address'], 'netobj': ip} - self.addNetworkHost(ip, host) - - if interfacedata.has_key('secondary'): - for netdata in interfacedata['secondary']: - ip = IPNetwork(netdata['address'] + '/' + netdata['netmask']) - host = {'hostname': hostname, 'address': netdata['address'], 'netobj': ip} - self.addNetworkHost(ip, host) - - if interfacedata.has_key('inet6'): - for netdata in interfacedata['inet6']: - ip = IPNetwork(netdata['address'] + '/' + netdata['prefixlen']) - host = {'hostname': hostname, 'address': netdata['address'], 'netobj': ip} - self.addNetworkHost(ip, host) - - - def addNetworkHost(self, ip, host): - if self.network_list.has_key(ip.cidr): - self.network_list[ip.cidr].append(host) - else: - self.network_list[ip.cidr] = [host] + self.stat_list = self.saltrun.cmd('manage.status') def createHostList(self): fo = open(self.config['base_path'] + "hostlist.html", "wb") env = Environment(loader = FileSystemLoader(config['template_path'])) template = env.get_template('hostlist_template.html') - fo.write(template.render({'hostlist': self.host_list})) + fo.write(template.render({'hostlist': self.host_list, 'statlist': self.stat_list})) fo.close - def createHostPages(self): - for hostname, hostdata in self.host_list.iteritems(): - self.createHostPage(hostname, hostdata) - - def createHostPage(self, hostname, hostdata): - fo = open(self.config['host_page_path'] + hostname + ".html", "wb") - env = Environment(loader = FileSystemLoader(config['template_path'])) - template = env.get_template('host_template.html') - fo.write(template.render(hostdata)) - fo.close - - def createNetworkList(self): - fo = open(self.config['base_path'] + "networklist.html", "wb") - env = Environment(loader = FileSystemLoader(config['template_path'])) - env.filters['len'] = self.lenFilter - template = env.get_template('networklist_template.html') - fo.write(template.render({'networklist': self.network_list})) - fo.close - - def createNetworkPages(self): - for network, networkdata in self.network_list.iteritems(): - self.createNetworkPage(network, networkdata) - - def createNetworkPage(self, network, networkdata): - fo = open(self.config['network_page_path'] + str(network.cidr).replace('/', '_') + ".html", "wb") - env = Environment(loader = FileSystemLoader(config['template_path'])) - env.filters['replace'] = self.replaceFilter - template = env.get_template('network_template.html') - fo.write(template.render({'network': network, 'data': networkdata})) - fo.close - def lenFilter(self, list): return len(list) diff --git a/templates/host_template.html b/templates/host_template.html deleted file mode 100644 index 59eb696..0000000 --- a/templates/host_template.html +++ /dev/null @@ -1,59 +0,0 @@ - - - {{ id }} - - - - - - - - -
- -
- - - -
- -
-
-

System information

-

Generic

- -

CPU

- -

Memory

- -

Storage

- -

Network

- -
- - diff --git a/templates/hostlist_template.html b/templates/hostlist_template.html index 8002c2f..4dab9d1 100644 --- a/templates/hostlist_template.html +++ b/templates/hostlist_template.html @@ -1,137 +1,137 @@ - Host list - - - - - - + + + + + + + + - -
- -
- - - -
- +
+
+

Inventory Salt minions list

+
-
-

Hosts

+
+
+
+

Available minions

+ + + + + + + + + + + + + + + + + + + + + + + {% for name, data in hostlist.iteritems() %} + + + + + + + + + + {% endfor %} + + + + + +
NameMachine typeOSRolesIPRAM (MB)# CPUs
NameMachine typeOSRolesIPRAM (MB)# CPUs
+ + {{ name }} + {% if data.virtual != "physical" %} Virtual ({{ data.virtual }}) {% else %} Physical {% endif %}{{ data.lsb_distrib_description }}{% for role in data.roles %} {{ role }} {% endfor %}{{ data.fqdn_ip }}{{ data.mem_total }}{{ data.num_cpus }}
Total: + + + + + + +
+

Disconnected minions

+ + + + + + + + {% for name in statlist %} + + + + {% endfor %} + +
Name
{{ name }}
+
+
+
+ + {% for name, data in hostlist.iteritems() %} + + {% endfor %} + - - - - - - - - - - - - - - {% for name, data in hostlist.iteritems() %} - - - - - - - {% endfor %} - - - - - - - - - -
NameOSRAM (MB)# CPUs
Name - OS - RAM - # CPUs -
{{ name }}{{ data.lsb_distrib_description }}{{ data.mem_total }}{{ data.num_cpus }}
-
- diff --git a/templates/network_template.html b/templates/network_template.html deleted file mode 100644 index 04383e7..0000000 --- a/templates/network_template.html +++ /dev/null @@ -1,60 +0,0 @@ - - - {{ network.cidr }} - - - - - - - - -
- -
- - - -
- -
-
-

Adresses

- - - - - - - - - - - {% for netdata in data %} - - - - - {% endfor %} - - - - - - - -
AddressHost
Address - Host -
{{ netdata['address'] }}{{ netdata['hostname'] }}
-
- - diff --git a/templates/networklist_template.html b/templates/networklist_template.html deleted file mode 100644 index 150fa97..0000000 --- a/templates/networklist_template.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - Network list - - - - - - - - - - -
- -
- - - -
- -
-
- -

Networks

- - - - - - - - - - - - {% for network, data in networklist.iteritems() %} - - - - - {% endfor %} - - - - - - - -
NetworkUsage
Network - Usage -
{{ network.cidr }}{{ data|len }}
-
- - - diff --git a/www/networks/.keep b/www/networks/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/www/res/inittable.js b/www/res/inittable.js new file mode 100644 index 0000000..5e8b934 --- /dev/null +++ b/www/res/inittable.js @@ -0,0 +1,68 @@ +$(document).ready(function() { + $('#hostlist thead td').each( function () { + var title = $('#hostlist thead th').eq( $(this).index() ).text(); + $(this).html( '' ); + } ); + + var table = $('#hostlist').DataTable({ + 'paging': false, + 'info': false, + 'dom': 't', + 'footerCallback': function ( row, data, start, end, display ) { + var api = this.api(), data; + + // Remove the formatting to get integer data for summation + var intVal = function ( i ) { + return typeof i === 'string' ? + i.replace(/[\$,]/g, '')*1 : + typeof i === 'number' ? + i : 0; + }; + + // Total over this page + hostsTotal = api + .rows() + .count(); + + // mem + memTotal = api + .column( 5, { page: 'current'} ) + .data() + .reduce( function (a, b) { + return intVal(a) + intVal(b); + }, 0 ); + // cpu + cpuTotal = api + .column( 6, { page: 'current'} ) + .data() + .reduce( function (a, b) { + return intVal(a) + intVal(b); + }, 0 ); + + // Update footer + $( api.column( 1 ).footer() ).html( + $.number(hostsTotal, 0, '.', ' ') + ); + + $( api.column( 5 ).footer() ).html( + $.number(memTotal, 0, '.', ' ') + ); + + $( api.column( 6 ).footer() ).html( + cpuTotal + ); + } + }); + + // Apply the search + table.columns().eq( 0 ).each( function ( colIdx ) { + $( 'input', table.column( colIdx ).header() ).on( 'keyup change', function () { + table + .column( colIdx ) + .search( this.value, true ) + .draw(); + } ); + } ); + + $('td.number').number( true, 0, '.', ' ' ); +});