#!/usr/bin/env python # Copyright (c) 2014 Hewlett-Packard Development Company, L.P. # Copyright 2016 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # TODO(mandre) # If possible get info from ironic for hosts prior to deployment import json import os import os_client_config import sys import traceback from oslo_config import cfg from six.moves import configparser from tripleo_common import inventory as inv from tripleo_common import inventories as invs from tripleo_validations import utils opts = [ cfg.StrOpt('host', help='List details about the specific host'), cfg.BoolOpt('list', help='List active hosts'), cfg.StrOpt('static-yaml-inventory', help=('output the active hosts ' 'to a static inventory ' '(yaml format) file.')), cfg.StrOpt('username', default=os.environ.get('OS_USERNAME')), cfg.StrOpt('password', default=os.environ.get('OS_PASSWORD')), cfg.StrOpt('auth-url', default=os.environ.get('OS_AUTH_URL')), cfg.StrOpt('auth-token', default=os.environ.get('OS_AUTH_TOKEN')), cfg.StrOpt('project-name', default=os.environ.get( 'OS_PROJECT_NAME', os.environ.get('OS_TENANT_NAME'))), cfg.StrOpt('cacert', default=os.environ.get('OS_CACERT')), cfg.ListOpt('plan', default=(os.environ.get('TRIPLEO_PLAN_NAME').split(',') if os.environ.get('TRIPLEO_PLAN_NAME') else None), help=('stack name(s) to use for generating the ' 'inventory data. If a comma delimited list ' 'of stacks is passed, the inventory will ' 'contain the union of those stacks.')), cfg.ListOpt('stack', default=None, help=('This arg has the same effect ' 'as --plan. If both are specified,' ' --stack will take precedence.')), cfg.StrOpt('ansible_ssh_user', default=os.environ.get('ANSIBLE_SSH_USER', 'heat-admin')), cfg.StrOpt('undercloud-connection', default=inv.UNDERCLOUD_CONNECTION_LOCAL, help=('Ansible connection to the undercloud, either "local" ' 'or "ssh". Defaults to "local".')), cfg.StrOpt('undercloud-key-file', default=None), cfg.StrOpt('ssh-network', default='ctlplane'), cfg.StrOpt('ansible_python_interpreter', default=None), cfg.BoolOpt('debug', help='Print tracebacks for exceptions'), cfg.StrOpt('serial', default=1), cfg.StrOpt('os-cloud', default=None, help=('Provide authentication ' 'with clouds.yaml file.')), ] def _parse_config(): default_config = os.environ.get('TRIPLEO_INVENTORY_CONFIG') if default_config: default_config = [default_config] configs = cfg.ConfigOpts() configs.register_cli_opts(opts) configs(prog='tripleo-ansible-inventory', default_config_files=default_config) if configs.auth_url is None and configs.os_cloud is None: print('ERROR: auth-url not defined and OS_AUTH_URL environment ' 'variable missing or --os-cloud option is not set, ' 'unable to proceed.', file=sys.stderr) sys.exit(1) if configs.auth_url: if '/v2.0' in configs.auth_url: configs.auth_url = configs.auth_url.replace('/v2.0', '/v3') return configs def write_static_inventory(inventory_file_path, inventory): # DEPRECATED any new features should be added to tripleo-common with open(inventory_file_path, 'w') as inventory_file: config = configparser.ConfigParser(allow_no_value=True) # Keep case formating. config.optionxform = str for section_name, section in inventory.items(): # NOTE(jaosorior): The section might be a list containing the # explicit list of nodes or a dict with several subsections. So # if it's a list, we process that and continue to the next # section. if isinstance(section, list): config.add_section(section_name) for host in section: config.set(section_name, host) continue if 'hosts' in section: config.add_section(section_name) for host in section['hosts']: config.set(section_name, host) if 'children' in section: children_section_name = "%s:%s" % (section_name, 'children') config.add_section(children_section_name) for child in section['children']: config.set(children_section_name, child) if 'vars' in section: vars_section_name = "%s:%s" % (section_name, 'vars') config.add_section(vars_section_name) for var, value in section['vars'].items(): if value is not None: config.set(vars_section_name, var, value) config.write(inventory_file) def main(): configs = _parse_config() auth_variables = {} if configs.auth_url: auth_variables.update({ 'auth_url': configs.auth_url, 'username': configs.username, 'project_name': configs.project_name, 'os_auth_token': configs.auth_token, 'password': configs.password, 'cacert': configs.cacert, 'timeout': 30 }) elif configs.os_cloud: config = os_client_config.OpenStackConfig() for cloud in config.get_all_clouds(): if cloud.name == configs.os_cloud: try: auth_variables.update({ 'auth_url': cloud.config['auth'].get('auth_url'), 'username': cloud.config['auth'].get('username'), 'project_name': cloud.config['auth'].get('project_name'), 'os_auth_token': cloud.config['auth'].get('auth_token'), 'password': cloud.config['auth'].get('password'), 'cacert': cloud.config.get('cacert'), 'timeout': cloud.config.get('api_timeout') }) except KeyError: raise KeyError("Missing values in clouds.yaml format") else: raise RuntimeError("No auth provided.") # Stack option takes precedence on plan: if configs.stack: plans = configs.stack elif configs.plan: plans = configs.plan else: plans = utils.list_plan_and_stack( utils.get_heat_client(auth_variables), utils.get_swift_client(auth_variables)) if plans is None: inventory = inv.TripleoInventory( session=utils.get_auth_session(auth_variables), hclient=utils.get_heat_client(auth_variables), auth_url=auth_variables.get('auth_url'), cacert=auth_variables.get('cacert'), project_name=auth_variables.get('project_name'), username=auth_variables.get('username'), ansible_ssh_user=configs.ansible_ssh_user, plan_name=plans, ansible_python_interpreter=configs.ansible_python_interpreter, undercloud_connection=configs.undercloud_connection, undercloud_key_file=configs.undercloud_key_file, host_network=configs.ssh_network, serial=configs.serial) else: inventory_map = {} for _plan in plans: inventory_map[str(_plan)] = inv.TripleoInventory( session=utils.get_auth_session(auth_variables), hclient=utils.get_heat_client(auth_variables), auth_url=auth_variables.get('auth_url'), cacert=auth_variables.get('cacert'), project_name=auth_variables.get('project_name'), username=auth_variables.get('username'), ansible_ssh_user=configs.ansible_ssh_user, plan_name=_plan, ansible_python_interpreter=configs.ansible_python_interpreter, undercloud_connection=configs.undercloud_connection, undercloud_key_file=configs.undercloud_key_file, host_network=configs.ssh_network, serial=configs.serial) inventory = invs.TripleoInventories(inventory_map) if configs.list: try: inventory_list = inventory.list() print(json.dumps(inventory_list)) except Exception as e: print("ERROR: Error creating inventory: {}".format(e), file=sys.stderr) if configs.debug: traceback.print_exc() sys.exit(1) elif configs.static_yaml_inventory: try: inventory.write_static_inventory(configs.static_yaml_inventory) except Exception as e: print("Error creating static inventory: {}".format(e), file=sys.stderr) if configs.debug: traceback.print_exc() sys.exit(1) elif configs.host: print(json.dumps(inventory.host())) sys.exit(0) if __name__ == '__main__': main()