181 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			181 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
| #!/usr/bin/python
 | |
| # -*- coding: utf-8 -*-
 | |
| """
 | |
| debops: run ansible-playbook with some customization
 | |
| """
 | |
| # Copyright (C) 2014-2015 Hartmut Goebel <h.goebel@crazy-compilers.com>
 | |
| # Part of the DebOps - https://debops.org/
 | |
| 
 | |
| # This program 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 program 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 program; if not,
 | |
| # write to the Free Software Foundation, Inc., 59
 | |
| # Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | |
| #
 | |
| # An on-line copy of the GNU General Public License can
 | |
| # be downloaded from the FSF web page at:
 | |
| # https://www.gnu.org/copyleft/gpl.html
 | |
| 
 | |
| from __future__ import print_function
 | |
| 
 | |
| import sys
 | |
| import os
 | |
| import ConfigParser
 | |
| 
 | |
| import ansible
 | |
| 
 | |
| from debops import *
 | |
| from debops.cmds import *
 | |
| 
 | |
| __author__ = "Hartmut Goebel <h.goebel@crazy-compilers.com>"
 | |
| __copyright__ = "Copyright 2014-2015 by Hartmut Goebel <h.goebel@crazy-compilers.com>"
 | |
| __licence__ = "GNU General Public License version 3 (GPL v3) or later"
 | |
| 
 | |
| 
 | |
| PATHSEP = ':'
 | |
| 
 | |
| ConfigFileHeader = """\
 | |
| # Ansible configuration file generated by DebOps, all changes will be lost.
 | |
| # You can manipulate the contents of this file via `.debops.cfg`.
 | |
| """
 | |
| 
 | |
| def write_config(filename, config):
 | |
|     cfgparser = ConfigParser.ConfigParser()
 | |
|     for section, pairs in config.items():
 | |
|         cfgparser.add_section(section)
 | |
|         for option, value in pairs.items():
 | |
|             cfgparser.set(section, option, value)
 | |
| 
 | |
|     with open(filename, "w") as fh:
 | |
|         print(ConfigFileHeader, file=fh)
 | |
|         cfgparser.write(fh)
 | |
| 
 | |
| 
 | |
| def gen_ansible_cfg(filename, config, project_root, playbooks_path,
 | |
|                     inventory_path):
 | |
|     # Generate Ansible configuration file
 | |
| 
 | |
|     def custom_paths(type):
 | |
|         if type in defaults:
 | |
|             # prepend value from .debops.cfg
 | |
|             yield defaults[type]
 | |
|         yield os.path.join(project_root, "ansible", type)
 | |
|         yield os.path.join(playbooks_path, type)
 | |
|         yield os.path.join("/usr/share/ansible/", type)
 | |
| 
 | |
|     # Add custom configuration options to ansible.cfg: Take values
 | |
|     # from [ansible ...] sections of the .debops.cfg file
 | |
|     # Note: To set debops default values, use debops.config.DEFAULTS
 | |
|     cfg = dict((sect.split(None, 1)[1], pairs)
 | |
|                for sect, pairs in config.items() if sect.startswith('ansible '))
 | |
| 
 | |
|     defaults = cfg.setdefault('defaults', {})
 | |
|     defaults['inventory'] = inventory_path
 | |
|     defaults['roles_path'] = PATHSEP.join(filter(None, (
 | |
|         defaults.get('roles_path'), # value from .debops.cfg or None
 | |
|         os.path.join(project_root, "roles"),
 | |
|         os.path.join(project_root, "ansible", "roles"),
 | |
|         os.path.join(playbooks_path, "..", "roles"),
 | |
|         os.path.join(playbooks_path, "roles"),
 | |
|         "/etc/ansible/roles")))
 | |
|     for plugin_type in ('action', 'callback', 'connection',
 | |
|                         'filter', 'lookup', 'vars'):
 | |
|         plugin_type = plugin_type+"_plugins"
 | |
|         defaults[plugin_type] = PATHSEP.join(custom_paths(plugin_type))
 | |
| 
 | |
|         if ansible.__version__ >= "1.7":
 | |
|             # work around a bug obviously introduced in 1.7, see
 | |
|             # https://github.com/ansible/ansible/issues/8555
 | |
|             if ' ' in defaults[plugin_type]:
 | |
|                 defaults[plugin_type] = PATHSEP.join(
 | |
|                     '"%s"' % p for p in defaults[plugin_type].split(PATHSEP))
 | |
| 
 | |
|     defaults['library'] = PATHSEP.join(custom_paths('library'))
 | |
| 
 | |
|     write_config(filename, cfg)
 | |
| 
 | |
| 
 | |
| def main(cmd_args):
 | |
|     project_root = find_debops_project(required=True)
 | |
|     config = read_config(project_root)
 | |
|     playbooks_path = find_playbookpath(config, project_root, required=True)
 | |
| 
 | |
|     # Make sure required commands are present
 | |
|     require_commands('ansible-playbook')
 | |
| 
 | |
|     def find_playbook(playbook):
 | |
|         tries = [
 | |
|             (project_root, "playbooks", playbook),
 | |
|             (project_root, "ansible", "playbooks", playbook),
 | |
|             (playbooks_path, playbook),
 | |
|             ]
 | |
| 
 | |
|         if 'playbooks_path' in config['paths']:
 | |
|             tries += [(custom_path, playbook) for custom_path in
 | |
|                       config['paths']['playbooks_path'].split(PATHSEP)]
 | |
| 
 | |
|         for parts in tries:
 | |
|             play = os.path.join(*parts)
 | |
|             if os.path.isfile(play):
 | |
|                 return play
 | |
| 
 | |
|     # Check if user specified a potential playbook name as the first
 | |
|     # argument. If yes, use it as the playbook name and remove it from
 | |
|     # the argument list
 | |
|     play = None
 | |
|     if len(cmd_args) > 0:
 | |
|         maybe_play = cmd_args[0]
 | |
|         if os.path.isfile(maybe_play):
 | |
|             play = maybe_play
 | |
|         else:
 | |
|             play = find_playbook(maybe_play + ".yml")
 | |
|         if play:
 | |
|             cmd_args.pop(0)
 | |
|         del maybe_play
 | |
|     if not play:
 | |
|         play = find_playbook("site.yml")
 | |
| 
 | |
|     inventory_path = find_inventorypath(project_root)
 | |
|     os.environ['ANSIBLE_HOSTS'] = inventory_path
 | |
| 
 | |
|     ansible_config_file = os.path.join(project_root, ANSIBLE_CONFIG_FILE)
 | |
|     os.environ['ANSIBLE_CONFIG'] = os.path.abspath(ansible_config_file)
 | |
|     gen_ansible_cfg(ansible_config_file, config, project_root, playbooks_path,
 | |
|                     inventory_path)
 | |
| 
 | |
|     # Allow insecure SSH connections if requested
 | |
|     if INSECURE:
 | |
|         os.environ['ANSIBLE_HOST_KEY_CHECKING'] = 'False'
 | |
| 
 | |
|     # Create path to EncFS encrypted directory, based on inventory name
 | |
|     encfs_encrypted = os.path.join(os.path.dirname(inventory_path),
 | |
|                                    ENCFS_PREFIX + SECRET_NAME)
 | |
| 
 | |
|     # Check if encrypted secret directory exists and use it
 | |
|     revert_unlock = padlock_unlock(encfs_encrypted)
 | |
|     try:
 | |
|         # Run ansible-playbook with custom environment
 | |
|         print("Running Ansible playbook from:")
 | |
|         print(play, "...")
 | |
|         return subprocess.call(['ansible-playbook', play] + cmd_args)
 | |
|     finally:
 | |
|         if revert_unlock:
 | |
|             padlock_lock(encfs_encrypted)
 | |
| 
 | |
| 
 | |
| try:
 | |
|     sys.exit(main(sys.argv[1:]))
 | |
| except KeyboardInterrupt:
 | |
|     raise SystemExit('... aborted')
 |