2017-01-17 20:01:58 +01:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
# Copyright (c) 2016, Hugh Ma <hugh.ma@flextronics.com>
|
|
|
|
#
|
|
|
|
# This module is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This software is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this software. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
# Stacki inventory script
|
|
|
|
# Configure stacki.yml with proper auth information and place in the following:
|
|
|
|
# - ../inventory/stacki.yml
|
|
|
|
# - /etc/stacki/stacki.yml
|
|
|
|
# - /etc/ansible/stacki.yml
|
|
|
|
# The stacki.yml file can contain entries for authentication information
|
|
|
|
# regarding the Stacki front-end node.
|
|
|
|
#
|
|
|
|
# use_hostnames uses hostname rather than interface ip as connection
|
|
|
|
#
|
|
|
|
#
|
|
|
|
|
|
|
|
"""
|
|
|
|
Example Usage:
|
|
|
|
List Stacki Nodes
|
|
|
|
$ ./stack.py --list
|
|
|
|
|
|
|
|
|
|
|
|
Example Configuration:
|
|
|
|
---
|
|
|
|
stacki:
|
|
|
|
auth:
|
|
|
|
stacki_user: admin
|
|
|
|
stacki_password: abc12345678910
|
|
|
|
stacki_endpoint: http://192.168.200.50/stack
|
|
|
|
use_hostnames: false
|
|
|
|
"""
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import yaml
|
|
|
|
from distutils.version import StrictVersion
|
|
|
|
|
|
|
|
try:
|
|
|
|
import json
|
|
|
|
except:
|
|
|
|
import simplejson as json
|
|
|
|
|
|
|
|
try:
|
|
|
|
import requests
|
|
|
|
except:
|
|
|
|
sys.exit('requests package is required for this inventory script')
|
2017-01-28 00:45:23 +01:00
|
|
|
|
2017-01-17 20:01:58 +01:00
|
|
|
|
|
|
|
CONFIG_FILES = ['/etc/stacki/stacki.yml', '/etc/ansible/stacki.yml']
|
|
|
|
|
|
|
|
|
|
|
|
def stack_auth(params):
|
|
|
|
endpoint = params['stacki_endpoint']
|
|
|
|
auth_creds = {'USERNAME': params['stacki_user'],
|
|
|
|
'PASSWORD': params['stacki_password']}
|
|
|
|
|
|
|
|
client = requests.session()
|
|
|
|
client.get(endpoint)
|
|
|
|
|
|
|
|
init_csrf = client.cookies['csrftoken']
|
|
|
|
|
|
|
|
header = {'csrftoken': init_csrf, 'X-CSRFToken': init_csrf,
|
|
|
|
'Content-type': 'application/x-www-form-urlencoded'}
|
|
|
|
|
|
|
|
login_endpoint = endpoint + "/login"
|
|
|
|
|
|
|
|
login_req = client.post(login_endpoint, data=auth_creds, headers=header)
|
|
|
|
|
|
|
|
csrftoken = login_req.cookies['csrftoken']
|
|
|
|
sessionid = login_req.cookies['sessionid']
|
|
|
|
|
|
|
|
auth_creds.update(CSRFTOKEN=csrftoken, SESSIONID=sessionid)
|
|
|
|
|
|
|
|
return client, auth_creds
|
|
|
|
|
|
|
|
|
|
|
|
def stack_build_header(auth_creds):
|
|
|
|
header = {'csrftoken': auth_creds['CSRFTOKEN'],
|
|
|
|
'X-CSRFToken': auth_creds['CSRFTOKEN'],
|
|
|
|
'sessionid': auth_creds['SESSIONID'],
|
|
|
|
'Content-type': 'application/json'}
|
|
|
|
|
|
|
|
return header
|
|
|
|
|
|
|
|
|
|
|
|
def stack_host_list(endpoint, header, client):
|
|
|
|
|
|
|
|
stack_r = client.post(endpoint, data=json.dumps({ "cmd": "list host"}),
|
|
|
|
headers=header)
|
|
|
|
return json.loads(stack_r.json())
|
|
|
|
|
|
|
|
|
|
|
|
def stack_net_list(endpoint, header, client):
|
|
|
|
|
|
|
|
stack_r = client.post(endpoint, data=json.dumps({ "cmd": "list host interface"}),
|
|
|
|
headers=header)
|
|
|
|
return json.loads(stack_r.json())
|
|
|
|
|
|
|
|
def format_meta(hostdata, intfdata, config):
|
|
|
|
use_hostnames = config['use_hostnames']
|
|
|
|
meta = dict(all=dict(hosts=list()),
|
|
|
|
frontends=dict(hosts=list()),
|
|
|
|
backends=dict(hosts=list()),
|
|
|
|
_meta=dict(hostvars=dict()))
|
|
|
|
|
|
|
|
# Iterate through list of dicts of hosts and remove
|
|
|
|
# environment key as it causes conflicts
|
|
|
|
for host in hostdata:
|
|
|
|
del host['environment']
|
|
|
|
meta['_meta']['hostvars'][host['host']] = host
|
|
|
|
meta['_meta']['hostvars'][host['host']]['interfaces'] = list()
|
|
|
|
|
|
|
|
# @bbyhuy to improve readability in next iteration
|
|
|
|
|
|
|
|
for intf in intfdata:
|
|
|
|
if intf['host'] in meta['_meta']['hostvars']:
|
|
|
|
meta['_meta']['hostvars'][intf['host']]['interfaces'].append(intf)
|
|
|
|
if intf['default'] is True:
|
|
|
|
meta['_meta']['hostvars'][intf['host']]['ansible_host'] = intf['ip']
|
|
|
|
if not use_hostnames:
|
|
|
|
meta['all']['hosts'].append(intf['ip'])
|
|
|
|
if meta['_meta']['hostvars'][intf['host']]['appliance'] != 'frontend':
|
|
|
|
meta['backends']['hosts'].append(intf['ip'])
|
|
|
|
else:
|
|
|
|
meta['frontends']['hosts'].append(intf['ip'])
|
|
|
|
else:
|
|
|
|
meta['all']['hosts'].append(intf['host'])
|
|
|
|
if meta['_meta']['hostvars'][intf['host']]['appliance'] != 'frontend':
|
|
|
|
meta['backends']['hosts'].append(intf['host'])
|
|
|
|
else:
|
|
|
|
meta['frontends']['hosts'].append(intf['host'])
|
|
|
|
return meta
|
|
|
|
|
|
|
|
|
|
|
|
def parse_args():
|
|
|
|
parser = argparse.ArgumentParser(description='Stacki Inventory Module')
|
|
|
|
group = parser.add_mutually_exclusive_group(required=True)
|
|
|
|
group.add_argument('--list', action='store_true',
|
|
|
|
help='List active hosts')
|
|
|
|
group.add_argument('--host', help='List details about the specific host')
|
|
|
|
|
|
|
|
return parser.parse_args()
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
args = parse_args()
|
|
|
|
|
|
|
|
|
|
|
|
if StrictVersion(requests.__version__) < StrictVersion("2.4.3"):
|
|
|
|
sys.exit('requests>=2.4.3 is required for this inventory script')
|
|
|
|
|
|
|
|
try:
|
|
|
|
config_files = CONFIG_FILES
|
|
|
|
config_files.append(os.path.dirname(os.path.realpath(__file__)) + '/stacki.yml')
|
|
|
|
config = None
|
|
|
|
for cfg_file in config_files:
|
|
|
|
if os.path.isfile(cfg_file):
|
|
|
|
stream = open(cfg_file, 'r')
|
|
|
|
config = yaml.load(stream)
|
|
|
|
break
|
|
|
|
if not config:
|
|
|
|
sys.stderr.write("No config file found at {}\n".format(config_files))
|
|
|
|
sys.exit(1)
|
|
|
|
client, auth_creds = stack_auth(config['stacki']['auth'])
|
|
|
|
header = stack_build_header(auth_creds)
|
|
|
|
host_list = stack_host_list(config['stacki']['auth']['stacki_endpoint'], header, client)
|
|
|
|
intf_list = stack_net_list(config['stacki']['auth']['stacki_endpoint'], header, client)
|
|
|
|
final_meta = format_meta(host_list, intf_list, config)
|
|
|
|
print(json.dumps(final_meta, indent=4))
|
|
|
|
except Exception as e:
|
|
|
|
sys.stderr.write('%s\n' % e.message)
|
|
|
|
sys.exit(1)
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|