1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com> 4# 5# This file is part of Ansible 6# 7# Ansible is free software: you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation, either version 3 of the License, or 10# (at your option) any later version. 11# 12# Ansible is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with Ansible. If not, see <http://www.gnu.org/licenses/>. 19 20# PYTHON_ARGCOMPLETE_OK 21 22from __future__ import (absolute_import, division, print_function) 23__metaclass__ = type 24 25__requires__ = ['ansible_base'] 26 27 28import errno 29import os 30import shutil 31import sys 32import traceback 33 34from ansible import context 35from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError 36from ansible.module_utils._text import to_text 37 38 39# Used for determining if the system is running a new enough python version 40# and should only restrict on our documented minimum versions 41_PY3_MIN = sys.version_info[:2] >= (3, 5) 42_PY2_MIN = (2, 6) <= sys.version_info[:2] < (3,) 43_PY_MIN = _PY3_MIN or _PY2_MIN 44if not _PY_MIN: 45 raise SystemExit('ERROR: Ansible requires a minimum of Python2 version 2.6 or Python3 version 3.5. Current version: %s' % ''.join(sys.version.splitlines())) 46 47 48class LastResort(object): 49 # OUTPUT OF LAST RESORT 50 def display(self, msg, log_only=None): 51 print(msg, file=sys.stderr) 52 53 def error(self, msg, wrap_text=None): 54 print(msg, file=sys.stderr) 55 56 57if __name__ == '__main__': 58 59 display = LastResort() 60 61 try: # bad ANSIBLE_CONFIG or config options can force ugly stacktrace 62 import ansible.constants as C 63 from ansible.utils.display import Display 64 except AnsibleOptionsError as e: 65 display.error(to_text(e), wrap_text=False) 66 sys.exit(5) 67 68 cli = None 69 me = os.path.basename(sys.argv[0]) 70 71 try: 72 display = Display() 73 display.debug("starting run") 74 75 sub = None 76 target = me.split('-') 77 if target[-1][0].isdigit(): 78 # Remove any version or python version info as downstreams 79 # sometimes add that 80 target = target[:-1] 81 82 if len(target) > 1: 83 sub = target[1] 84 myclass = "%sCLI" % sub.capitalize() 85 elif target[0] == 'ansible': 86 sub = 'adhoc' 87 myclass = 'AdHocCLI' 88 else: 89 raise AnsibleError("Unknown Ansible alias: %s" % me) 90 91 try: 92 mycli = getattr(__import__("ansible.cli.%s" % sub, fromlist=[myclass]), myclass) 93 except ImportError as e: 94 # ImportError members have changed in py3 95 if 'msg' in dir(e): 96 msg = e.msg 97 else: 98 msg = e.message 99 if msg.endswith(' %s' % sub): 100 raise AnsibleError("Ansible sub-program not implemented: %s" % me) 101 else: 102 raise 103 104 b_ansible_dir = os.path.expanduser(os.path.expandvars(b"~/.ansible")) 105 try: 106 os.mkdir(b_ansible_dir, 0o700) 107 except OSError as exc: 108 if exc.errno != errno.EEXIST: 109 display.warning("Failed to create the directory '%s': %s" 110 % (to_text(b_ansible_dir, errors='surrogate_or_replace'), 111 to_text(exc, errors='surrogate_or_replace'))) 112 else: 113 display.debug("Created the '%s' directory" % to_text(b_ansible_dir, errors='surrogate_or_replace')) 114 115 try: 116 args = [to_text(a, errors='surrogate_or_strict') for a in sys.argv] 117 except UnicodeError: 118 display.error('Command line args are not in utf-8, unable to continue. Ansible currently only understands utf-8') 119 display.display(u"The full traceback was:\n\n%s" % to_text(traceback.format_exc())) 120 exit_code = 6 121 else: 122 cli = mycli(args) 123 exit_code = cli.run() 124 125 except AnsibleOptionsError as e: 126 cli.parser.print_help() 127 display.error(to_text(e), wrap_text=False) 128 exit_code = 5 129 except AnsibleParserError as e: 130 display.error(to_text(e), wrap_text=False) 131 exit_code = 4 132# TQM takes care of these, but leaving comment to reserve the exit codes 133# except AnsibleHostUnreachable as e: 134# display.error(str(e)) 135# exit_code = 3 136# except AnsibleHostFailed as e: 137# display.error(str(e)) 138# exit_code = 2 139 except AnsibleError as e: 140 display.error(to_text(e), wrap_text=False) 141 exit_code = 1 142 except KeyboardInterrupt: 143 display.error("User interrupted execution") 144 exit_code = 99 145 except Exception as e: 146 if C.DEFAULT_DEBUG: 147 # Show raw stacktraces in debug mode, It also allow pdb to 148 # enter post mortem mode. 149 raise 150 have_cli_options = bool(context.CLIARGS) 151 display.error("Unexpected Exception, this is probably a bug: %s" % to_text(e), wrap_text=False) 152 if not have_cli_options or have_cli_options and context.CLIARGS['verbosity'] > 2: 153 log_only = False 154 if hasattr(e, 'orig_exc'): 155 display.vvv('\nexception type: %s' % to_text(type(e.orig_exc))) 156 why = to_text(e.orig_exc) 157 if to_text(e) != why: 158 display.vvv('\noriginal msg: %s' % why) 159 else: 160 display.display("to see the full traceback, use -vvv") 161 log_only = True 162 display.display(u"the full traceback was:\n\n%s" % to_text(traceback.format_exc()), log_only=log_only) 163 exit_code = 250 164 165 sys.exit(exit_code) 166