Example #1
0
    def run(self):

        results = None

        super(InventoryCLI, self).run()

        # Initialize needed objects
        if getattr(self, '_play_prereqs', False):
            self.loader, self.inventory, self.vm = self._play_prereqs(self.options)
        else:
            # fallback to pre 2.4 way of initialzing
            from ansible.vars import VariableManager
            from ansible.inventory import Inventory

            self._new_api = False
            self.loader = DataLoader()
            self.vm = VariableManager()

            # use vault if needed
            if self.options.vault_password_file:
                vault_pass = CLI.read_vault_password_file(self.options.vault_password_file, loader=self.loader)
            elif self.options.ask_vault_pass:
                vault_pass = self.ask_vault_passwords()
            else:
                vault_pass = None

            if vault_pass:
                self.loader.set_vault_password(vault_pass)
                # actually get inventory and vars

            self.inventory = Inventory(loader=self.loader, variable_manager=self.vm, host_list=self.options.inventory)
            self.vm.set_inventory(self.inventory)

        if self.options.host:
            hosts = self.inventory.get_hosts(self.options.host)
            if len(hosts) != 1:
                raise AnsibleOptionsError("You must pass a single valid host to --hosts parameter")

            myvars = self._get_host_variables(host=hosts[0])
            self._remove_internal(myvars)

            # FIXME: should we template first?
            results = self.dump(myvars)

        elif self.options.graph:
            results = self.inventory_graph()
        elif self.options.list:
            top = self._get_group('all')
            if self.options.yaml:
                results = self.yaml_inventory(top)
            else:
                results = self.json_inventory(top)
            results = self.dump(results)

        if results:
            # FIXME: pager?
            display.display(results)
            exit(0)

        exit(1)
Example #2
0
    def fetch(self, role_data):
        """
        Downloads the archived role from github to a temp location
        """
        if role_data:

            # first grab the file and save it to a temp location
            if "github_user" in role_data and "github_repo" in role_data:
                archive_url = 'https://github.com/%s/%s/archive/%s.tar.gz' % (role_data["github_user"], role_data["github_repo"], self.version)
            else:
                archive_url = self.src

            display.display("- downloading role from %s" % archive_url)

            try:
                url_file = open_url(archive_url, validate_certs=self._validate_certs)
                temp_file = tempfile.NamedTemporaryFile(delete=False)
                data = url_file.read()
                while data:
                    temp_file.write(data)
                    data = url_file.read()
                temp_file.close()
                return temp_file.name
            except Exception as e:
                display.error("failed to download the file: %s" % str(e))

        return False
Example #3
0
    def _start_connection(self):
        '''
        Starts the persistent connection
        '''
        master, slave = pty.openpty()

        python = sys.executable

        def find_file_in_path(filename):
            # Check $PATH first, followed by same directory as sys.argv[0]
            paths = os.environ['PATH'].split(os.pathsep) + [os.path.dirname(sys.argv[0])]
            for dirname in paths:
                fullpath = os.path.join(dirname, filename)
                if os.path.isfile(fullpath):
                    return fullpath

            raise AnsibleError("Unable to find location of '%s'" % filename)

        p = subprocess.Popen(
            [python, find_file_in_path('ansible-connection'), to_text(os.getppid())],
            stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE
        )
        stdin = os.fdopen(master, 'wb', 0)
        os.close(slave)

        # Need to force a protocol that is compatible with both py2 and py3.
        # That would be protocol=2 or less.
        # Also need to force a protocol that excludes certain control chars as
        # stdin in this case is a pty and control chars will cause problems.
        # that means only protocol=0 will work.
        src = cPickle.dumps(self._play_context.serialize(), protocol=0)
        stdin.write(src)

        stdin.write(b'\n#END_INIT#\n')
        stdin.flush()

        (stdout, stderr) = p.communicate()
        stdin.close()

        if p.returncode == 0:
            result = json.loads(to_text(stdout, errors='surrogate_then_replace'))
        else:
            try:
                result = json.loads(to_text(stderr, errors='surrogate_then_replace'))
            except getattr(json.decoder, 'JSONDecodeError', ValueError):
                # JSONDecodeError only available on Python 3.5+
                result = {'error': to_text(stderr, errors='surrogate_then_replace')}

        if 'messages' in result:
            for msg in result.get('messages'):
                display.vvvv('%s' % msg, host=self._play_context.remote_addr)

        if 'error' in result:
            if self._play_context.verbosity > 2:
                if result.get('exception'):
                    msg = "The full traceback is:\n" + result['exception']
                    display.display(msg, color=C.COLOR_ERROR)
            raise AnsibleError(result['error'])

        return result['socket_path']
Example #4
0
 def do_forks(self, arg):
     """Set the number of forks"""
     if not arg:
         display.display('Usage: forks <number>')
         return
     self.options.forks = int(arg)
     self.set_prompt()
Example #5
0
    def _print_section(text, values):
        if len(values) == 0:
            return

        display.display("{}:".format(text), color=C.COLOR_UNREACHABLE)
        for name in values:
            display.display(" {}".format(name), color=C.COLOR_VERBOSE)
Example #6
0
 def do_remote_user(self, arg):
     """Given a username, set the remote user plays are run by"""
     if arg:
         self.options.remote_user = arg
         self.set_prompt()
     else:
         display.display("Please specify a remote user, e.g. `remote_user root`")
Example #7
0
 def do_verbosity(self, arg):
     """Set verbosity level"""
     if not arg:
         display.display('Usage: verbosity <number>')
     else:
         display.verbosity = int(arg)
         display.v('verbosity level set to %s' % arg)
Example #8
0
 def do_become_method(self, arg):
     """Given a become_method, set the privilege escalation method when using become"""
     if arg:
         self.options.become_method = arg
         display.v("become_method changed to %s" % self.options.become_method)
     else:
         display.display("Please specify a become_method, e.g. `become_method su`")
Example #9
0
 def do_check(self, arg):
     """Toggle whether plays run with check mode"""
     if arg:
         self.options.check = C.mk_boolean(arg)
         display.v("check mode changed to %s" % self.options.check)
     else:
         display.display("Please specify check mode value, e.g. `check yes`")
Example #10
0
 def do_diff(self, arg):
     """Toggle whether plays run with diff"""
     if arg:
         self.options.diff = C.mk_boolean(arg)
         display.v("diff mode changed to %s" % self.options.diff)
     else:
         display.display("Please specify a diff value , e.g. `diff yes`")
Example #11
0
    def execute_login(self):
        """
        verify user's identify via Github and retrieve an auth token from Ansible Galaxy.
        """
        # Authenticate with github and retrieve a token
        if self.options.token is None:
            if C.GALAXY_TOKEN:
                github_token = C.GALAXY_TOKEN
            else:
                login = GalaxyLogin(self.galaxy)
                github_token = login.create_github_token()
        else:
            github_token = self.options.token

        galaxy_response = self.api.authenticate(github_token)

        if self.options.token is None and C.GALAXY_TOKEN is None:
            # Remove the token we created
            login.remove_github_token()

        # Store the Galaxy token
        token = GalaxyToken()
        token.set(galaxy_response['token'])

        display.display("Successfully logged into Galaxy as %s" % galaxy_response['username'])
        return 0
Example #12
0
    def run(self):

        if self.options.verbosity > 0:
            if C.CONFIG_FILE:
                display.display("Using %s as config file" % C.CONFIG_FILE)
            else:
                display.display("No config file found; using defaults")
Example #13
0
    def __init__(self, play_context, new_stdin, *args, **kwargs):
        super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)

        self._network_os = self._play_context.network_os or 'default'
        display.display('network_os is set to %s' % self._network_os, log_only=True)

        self._manager = None
        self._connected = False
Example #14
0
 def do_become_user(self, arg):
     """Given a username, set the user that plays are run by when using become"""
     if arg:
         self.options.become_user = arg
     else:
         display.display("Please specify a user, e.g. `become_user jenkins`")
         display.v("Current user is %s" % self.options.become_user)
     self.set_prompt()
Example #15
0
    def execute_rekey(self):
        ''' re-encrypt a vaulted file with a new secret, the previous secret is required '''
        for f in self.args:
            # FIXME: plumb in vault_id, use the default new_vault_secret for now
            self.editor.rekey_file(f, self.new_encrypt_secret,
                                   self.new_encrypt_vault_id)

        display.display("Rekey successful", stderr=True)
Example #16
0
 def do_become(self, arg):
     """Toggle whether plays run with become"""
     if arg:
         self.options.become = C.mk_boolean(arg)
         display.v("become changed to %s" % self.options.become)
         self.set_prompt()
     else:
         display.display("Please specify become value, e.g. `become yes`")
Example #17
0
 def do_list(self, arg):
     """List the hosts in the current group"""
     if arg == 'groups':
         for group in self.groups:
             display.display(group)
     else:
         for host in self.selected:
             display.display(host.name)
Example #18
0
    def _run(self):
        args = {"dest": self._get_param_dest(), "repo": self._get_param_repo()}
        result = self._call_module(name="update_git", args=args)

        show_warning = result.get("show_warning")
        if show_warning is not None:
            display.display(show_warning, color=C.COLOR_WARN)

        return result
Example #19
0
    def execute_rekey(self):
        for f in self.args:
            if not (os.path.isfile(f)):
                raise AnsibleError(f + " does not exist")

        for f in self.args:
            self.editor.rekey_file(f, self.new_vault_pass)

        display.display("Rekey successful", stderr=True)
Example #20
0
    def execute_decrypt(self):

        if len(self.args) == 0 and sys.stdin.isatty():
            display.display("Reading ciphertext input from stdin", stderr=True)

        for f in self.args or ['-']:
            self.editor.decrypt_file(f, output_file=self.options.output_file)

        if sys.stdout.isatty():
            display.display("Decryption successful", stderr=True)
Example #21
0
    def _load_playbook_data(self, file_name, variable_manager, vars=None):

        if os.path.isabs(file_name):
            self._basedir = os.path.dirname(file_name)
        else:
            self._basedir = os.path.normpath(os.path.join(self._basedir, os.path.dirname(file_name)))

        # set the loaders basedir
        cur_basedir = self._loader.get_basedir()
        self._loader.set_basedir(self._basedir)

        self._file_name = file_name

        # dynamically load any plugins from the playbook directory
        for name, obj in get_all_plugin_loaders():
            if obj.subdir:
                plugin_path = os.path.join(self._basedir, obj.subdir)
                if os.path.isdir(plugin_path):
                    obj.add_directory(plugin_path)

        try:
            ds = self._loader.load_from_file(os.path.basename(file_name))
        except UnicodeDecodeError as e:
            raise AnsibleParserError("Could not read playbook (%s) due to encoding issues: %s" % (file_name, to_native(e)))

        if not isinstance(ds, list):
            # restore the basedir in case this error is caught and handled
            self._loader.set_basedir(cur_basedir)
            raise AnsibleParserError("playbooks must be a list of plays", obj=ds)

        # Parse the playbook entries. For plays, we simply parse them
        # using the Play() object, and includes are parsed using the
        # PlaybookInclude() object
        for entry in ds:
            if not isinstance(entry, dict):
                # restore the basedir in case this error is caught and handled
                self._loader.set_basedir(cur_basedir)
                raise AnsibleParserError("playbook entries must be either a valid play or an include statement", obj=entry)

            if any(action in entry for action in ('import_playbook', 'include')):
                if 'include' in entry:
                    display.deprecated("'include' for playbook includes. You should use 'import_playbook' instead", version="2.8")
                pb = PlaybookInclude.load(entry, basedir=self._basedir, variable_manager=variable_manager, loader=self._loader)
                if pb is not None:
                    self._entries.extend(pb._entries)
                else:
                    which = entry.get('import_playbook', entry.get('include', entry))
                    display.display("skipping playbook '%s' due to conditional test failure" % which, color=C.COLOR_SKIP)
            else:
                entry_obj = Play.load(entry, variable_manager=variable_manager, loader=self._loader, vars=vars)
                self._entries.append(entry_obj)

        # we're done, so restore the old basedir in the loader
        self._loader.set_basedir(cur_basedir)
Example #22
0
 def send(self, command, prompts=None, answer=None, send_only=False):
     """Sends the command to the device in the opened shell"""
     try:
         self._history.append(command)
         self._shell.sendall(b'%s\r' % command)
         if send_only:
             return
         return self.receive(command, prompts, answer)
     except (socket.timeout, AttributeError):
         display.display(traceback.format_exc(), log_only=True)
         raise AnsibleConnectionFailure("timeout trying to send command: %s" % command.strip())
Example #23
0
    def execute_decrypt(self):
        ''' decrypt the supplied file using the provided vault secret '''

        if len(self.args) == 0 and sys.stdin.isatty():
            display.display("Reading ciphertext input from stdin", stderr=True)

        for f in self.args or ['-']:
            self.editor.decrypt_file(f, output_file=self.options.output_file)

        if sys.stdout.isatty():
            display.display("Decryption successful", stderr=True)
Example #24
0
 def evaluate(self, args):
     try:
         return eval(args, globals(), self.scope)
     except:
         t, v = sys.exc_info()[:2]
         if isinstance(t, str):
             exc_type_name = t
         else:
             exc_type_name = t.__name__
         display.display('***%s:%s' % (exc_type_name, repr(v)))
         raise
Example #25
0
    def run(self):
        """Run the ansible command

        Subclasses must implement this method.  It does the actual work of
        running an Ansible command.
        """

        if self.options.verbosity > 0:
            if C.CONFIG_FILE:
                display.display(u"Using %s as config file" % to_text(C.CONFIG_FILE))
            else:
                display.display(u"No config file found; using defaults")
Example #26
0
 def execute(self, args):
     try:
         code = compile(args + '\n', '<stdin>', 'single')
         exec(code, globals(), self.scope)
     except:
         t, v = sys.exc_info()[:2]
         if type(t) == type(''):
             exc_type_name = t
         else:
             exc_type_name = t.__name__
         display.display('***%s:%s' % (exc_type_name, repr(v)))
         raise
Example #27
0
    def _lookup(self, name, *args, **kwargs):
        instance = self._lookup_loader.get(name.lower(), loader=self._loader, templar=self)

        if instance is not None:
            wantlist = kwargs.pop('wantlist', False)
            allow_unsafe = kwargs.pop('allow_unsafe', C.DEFAULT_ALLOW_UNSAFE_LOOKUPS)
            errors = kwargs.pop('errors', 'strict')

            from ansible.utils.listify import listify_lookup_plugin_terms
            loop_terms = listify_lookup_plugin_terms(terms=args, templar=self, loader=self._loader, fail_on_undefined=True, convert_bare=False)
            # safely catch run failures per #5059
            try:
                ran = instance.run(loop_terms, variables=self._available_variables, **kwargs)
            except (AnsibleUndefinedVariable, UndefinedError) as e:
                raise AnsibleUndefinedVariable(e)
            except Exception as e:
                if self._fail_on_lookup_errors:
                    msg = u"An unhandled exception occurred while running the lookup plugin '%s'. Error was a %s, original message: %s" % \
                          (name, type(e), to_text(e))
                    if errors == 'warn':
                        display.warning(msg)
                    elif errors == 'ignore':
                        display.display(msg, log_only=True)
                    else:
                        raise AnsibleError(to_native(msg))
                ran = None

            if ran and not allow_unsafe:
                if wantlist:
                    ran = wrap_var(ran)
                else:
                    try:
                        ran = UnsafeProxy(",".join(ran))
                    except TypeError:
                        # Lookup Plugins should always return lists.  Throw an error if that's not
                        # the case:
                        if not isinstance(ran, Sequence):
                            raise AnsibleError("The lookup plugin '%s' did not return a list."
                                               % name)

                        # The TypeError we can recover from is when the value *inside* of the list
                        # is not a string
                        if len(ran) == 1:
                            ran = wrap_var(ran[0])
                        else:
                            ran = wrap_var(ran)

                if self.cur_context:
                    self.cur_context.unsafe = True
            return ran
        else:
            raise AnsibleError("lookup plugin (%s) not found" % name)
Example #28
0
    def update_play_context(self, play_context):
        """Updates the play context information for the connection"""

        display.display('updating play_context for connection', log_only=True)

        if self._play_context.become is False and play_context.become is True:
            auth_pass = play_context.become_pass
            self._terminal.on_authorize(passwd=auth_pass)

        elif self._play_context.become is True and not play_context.become:
            self._terminal.on_deauthorize()

        self._play_context = play_context
Example #29
0
    def execute_encrypt(self):
        ''' encrypt the supplied file using the provided vault secret '''

        if len(self.args) == 0 and sys.stdin.isatty():
            display.display("Reading plaintext input from stdin", stderr=True)

        for f in self.args or ['-']:
            # Fixme: use the correct vau
            self.editor.encrypt_file(f, self.encrypt_secret,
                                     vault_id=self.encrypt_vault_id,
                                     output_file=self.options.output_file)

        if sys.stdout.isatty():
            display.display("Encryption successful", stderr=True)
Example #30
0
    def _load_playbook_data(self, file_name, variable_manager):

        if os.path.isabs(file_name):
            self._basedir = os.path.dirname(file_name)
        else:
            self._basedir = os.path.normpath(os.path.join(self._basedir, os.path.dirname(file_name)))

        # set the loaders basedir
        cur_basedir = self._loader.get_basedir()
        self._loader.set_basedir(self._basedir)

        self._file_name = file_name

        # dynamically load any plugins from the playbook directory
        for name, obj in get_all_plugin_loaders():
            if obj.subdir:
                plugin_path = os.path.join(self._basedir, obj.subdir)
                if os.path.isdir(plugin_path):
                    obj.add_directory(plugin_path)

        ds = self._loader.load_from_file(os.path.basename(file_name))
        if not isinstance(ds, list):
            # restore the basedir in case this error is caught and handled
            self._loader.set_basedir(cur_basedir)
            raise AnsibleParserError("playbooks must be a list of plays", obj=ds)

        # Parse the playbook entries. For plays, we simply parse them
        # using the Play() object, and includes are parsed using the
        # PlaybookInclude() object
        for entry in ds:
            if not isinstance(entry, dict):
                # restore the basedir in case this error is caught and handled
                self._loader.set_basedir(cur_basedir)
                raise AnsibleParserError("playbook entries must be either a valid play or an include statement", obj=entry)

            if 'include' in entry:
                pb = PlaybookInclude.load(entry, basedir=self._basedir, variable_manager=variable_manager, loader=self._loader)
                if pb is not None:
                    self._entries.extend(pb._entries)
                else:
                    display.display("skipping playbook include '%s' due to conditional test failure" % entry.get('include', entry), color=C.COLOR_SKIP)
            else:
                entry_obj = Play.load(entry, variable_manager=variable_manager, loader=self._loader)
                self._entries.append(entry_obj)

        # we're done, so restore the old basedir in the loader
        self._loader.set_basedir(cur_basedir)
    def run(self):
        '''
        Run the given playbook, based on the settings in the play which
        may limit the runs to serialized groups, etc.
        '''

        signal.signal(signal.SIGINT, self._cleanup)

        result = 0
        entrylist = []
        entry = {}
        try:
            for playbook_path in self._playbooks:
                pb = Playbook.load(playbook_path,
                                   variable_manager=self._variable_manager,
                                   loader=self._loader)
                self._inventory.set_playbook_basedir(
                    os.path.dirname(playbook_path))

                if self._tqm is None:  # we are doing a listing
                    entry = {'playbook': playbook_path}
                    entry['plays'] = []

                i = 1
                plays = pb.get_plays()
                display.vv('%d plays in %s' % (len(plays), playbook_path))

                for play in plays:
                    if play._included_path is not None:
                        self._loader.set_basedir(play._included_path)
                    else:
                        self._loader.set_basedir(pb._basedir)

                    # clear any filters which may have been applied to the inventory
                    self._inventory.remove_restriction()

                    if play.vars_prompt:
                        for var in play.vars_prompt:
                            vname = var['name']
                            prompt = var.get("prompt", vname)
                            default = var.get("default", None)
                            private = var.get("private", True)
                            confirm = var.get("confirm", False)
                            encrypt = var.get("encrypt", None)
                            salt_size = var.get("salt_size", None)
                            salt = var.get("salt", None)

                            if vname not in play.vars:
                                if self._tqm:
                                    self._tqm.send_callback(
                                        'v2_playbook_on_vars_prompt', vname,
                                        private, prompt, encrypt, confirm,
                                        salt_size, salt, default)
                                if self._options.syntax:
                                    play.vars[vname] = default
                                else:
                                    play.vars[vname] = self._do_var_prompt(
                                        vname, private, prompt, encrypt,
                                        confirm, salt_size, salt, default)

                    # Create a temporary copy of the play here, so we can run post_validate
                    # on it without the templating changes affecting the original object.
                    all_vars = self._variable_manager.get_vars(
                        loader=self._loader, play=play)
                    templar = Templar(loader=self._loader, variables=all_vars)
                    new_play = play.copy()
                    new_play.post_validate(templar)

                    if self._options.syntax:
                        continue

                    if self._tqm is None:
                        # we are just doing a listing
                        entry['plays'].append(new_play)

                    else:
                        # make sure the tqm has callbacks loaded
                        self._tqm.load_callbacks()
                        self._tqm._unreachable_hosts.update(
                            self._unreachable_hosts)

                        # we are actually running plays
                        for batch in self._get_serialized_batches(new_play):
                            if len(batch) == 0:
                                self._tqm.send_callback(
                                    'v2_playbook_on_play_start', new_play)
                                self._tqm.send_callback(
                                    'v2_playbook_on_no_hosts_matched')
                                break

                            # restrict the inventory to the hosts in the serialized batch
                            self._inventory.restrict_to_hosts(batch)
                            # and run it...
                            result = self._tqm.run(play=play)

                            # check the number of failures here, to see if they're above the maximum
                            # failure percentage allowed, or if any errors are fatal. If either of those
                            # conditions are met, we break out, otherwise we only break out if the entire
                            # batch failed
                            failed_hosts_count = len(
                                self._tqm._failed_hosts) + len(
                                    self._tqm._unreachable_hosts)
                            if new_play.any_errors_fatal and failed_hosts_count > 0:
                                break
                            elif new_play.max_fail_percentage is not None and \
                               int((new_play.max_fail_percentage)/100.0 * len(batch)) > int((len(batch) - failed_hosts_count) / len(batch) * 100.0):
                                break
                            elif len(batch) == failed_hosts_count:
                                break

                            # clear the failed hosts dictionaires in the TQM for the next batch
                            self._unreachable_hosts.update(
                                self._tqm._unreachable_hosts)
                            self._tqm.clear_failed_hosts()

                        # if the last result wasn't zero or 3 (some hosts were unreachable),
                        # break out of the serial batch loop
                        if result not in (0, 3):
                            break

                    i = i + 1  # per play

                if entry:
                    entrylist.append(entry)  # per playbook

                # if the last result wasn't zero, break out of the playbook file name loop
                if result != 0:
                    break

            if entrylist:
                return entrylist

        finally:
            if self._tqm is not None:
                self._cleanup()

        if self._options.syntax:
            display.display("No issues encountered")
            return result

        # TODO: this stat summary stuff should be cleaned up and moved
        #        to a new method, if it even belongs here...
        display.banner("PLAY RECAP")

        hosts = sorted(self._tqm._stats.processed.keys())
        for h in hosts:
            t = self._tqm._stats.summarize(h)

            display.display(
                u"%s : %s %s %s %s" %
                (hostcolor(h, t), colorize(u'ok', t['ok'], 'green'),
                 colorize(u'changed', t['changed'], 'yellow'),
                 colorize(u'unreachable', t['unreachable'],
                          'red'), colorize(u'failed', t['failures'], 'red')),
                screen_only=True)

            display.display(
                u"%s : %s %s %s %s" %
                (hostcolor(h, t, False), colorize(u'ok', t['ok'], None),
                 colorize(u'changed', t['changed'], None),
                 colorize(u'unreachable', t['unreachable'],
                          None), colorize(u'failed', t['failures'], None)),
                log_only=True)

        display.display("", screen_only=True)
        # END STATS STUFF

        return result
Example #32
0
    def run(self):

        super(PlaybookCLI, self).run()

        # Note: slightly wrong, this is written so that implicit localhost
        # Manage passwords
        sshpass = None
        becomepass = None
        passwords = {}

        if self.options.role_file:
            args = ["install"]
            if self.options.ignore_errors:
                args.append("--ignore-errors")

            force = ""
            if self.options.force:
                args.append("--force")

            if self.options.roles_path:
                args.extend(["--roles-path", self.options.roles_path])

            if self.options.no_deps:
                args.append("--no-deps")

            args.extend(["--role-file", self.options.role_file])
            gc = GalaxyCLI(args=args)
            gc.parse()
            gc.run()

        # initial error check, to make sure all specified playbooks are accessible
        # before we start running anything through the playbook executor
        for playbook in self.args:
            if not os.path.exists(playbook):
                raise AnsibleError("the playbook: %s could not be found" %
                                   playbook)
            if not (os.path.isfile(playbook)
                    or stat.S_ISFIFO(os.stat(playbook).st_mode)):
                raise AnsibleError(
                    "the playbook: %s does not appear to be a file" % playbook)

        # don't deal with privilege escalation or passwords when we don't need to
        if not self.options.listhosts and not self.options.listtasks and not self.options.listtags and not self.options.syntax:
            self.normalize_become_options()
            (sshpass, becomepass) = self.ask_passwords()
            passwords = {'conn_pass': sshpass, 'become_pass': becomepass}

        loader, inventory, variable_manager = self._play_prereqs(self.options)

        # (which is not returned in list_hosts()) is taken into account for
        # warning if inventory is empty.  But it can't be taken into account for
        # checking if limit doesn't match any hosts.  Instead we don't worry about
        # limit if only implicit localhost was in inventory to start with.
        #
        # Fix this when we rewrite inventory by making localhost a real host (and thus show up in list_hosts())
        no_hosts = False
        if len(inventory.list_hosts()) == 0:
            # Empty inventory
            display.warning(
                "provided hosts list is empty, only localhost is available")
            no_hosts = True
        inventory.subset(self.options.subset)
        if len(inventory.list_hosts()) == 0 and no_hosts is False:
            # Invalid limit
            raise AnsibleError("Specified --limit does not match any hosts")

        # flush fact cache if requested
        if self.options.flush_cache:
            self._flush_cache(inventory, variable_manager)

        # create the playbook executor, which manages running the plays via a task queue manager
        pbex = PlaybookExecutor(playbooks=self.args,
                                inventory=inventory,
                                variable_manager=variable_manager,
                                loader=loader,
                                options=self.options,
                                passwords=passwords)

        results = pbex.run()

        if isinstance(results, list):
            for p in results:

                display.display('\nplaybook: %s' % p['playbook'])
                for idx, play in enumerate(p['plays']):
                    if play._included_path is not None:
                        loader.set_basedir(play._included_path)
                    else:
                        pb_dir = os.path.realpath(
                            os.path.dirname(p['playbook']))
                        loader.set_basedir(pb_dir)

                    msg = "\n  play #%d (%s): %s" % (idx + 1, ','.join(
                        play.hosts), play.name)
                    mytags = set(play.tags)
                    msg += '\tTAGS: [%s]' % (','.join(mytags))

                    if self.options.listhosts:
                        playhosts = set(inventory.get_hosts(play.hosts))
                        msg += "\n    pattern: %s\n    hosts (%d):" % (
                            play.hosts, len(playhosts))
                        for host in playhosts:
                            msg += "\n      %s" % host

                    display.display(msg)

                    all_tags = set()
                    if self.options.listtags or self.options.listtasks:
                        taskmsg = ''
                        if self.options.listtasks:
                            taskmsg = '    tasks:\n'

                        def _process_block(b):
                            taskmsg = ''
                            for task in b.block:
                                if isinstance(task, Block):
                                    taskmsg += _process_block(task)
                                else:
                                    if task.action == 'meta':
                                        continue

                                    all_tags.update(task.tags)
                                    if self.options.listtasks:
                                        cur_tags = list(
                                            mytags.union(set(task.tags)))
                                        cur_tags.sort()
                                        if task.name:
                                            taskmsg += "      %s" % task.get_name(
                                            )
                                        else:
                                            taskmsg += "      %s" % task.action
                                        taskmsg += "\tTAGS: [%s]\n" % ', '.join(
                                            cur_tags)

                            return taskmsg

                        all_vars = variable_manager.get_vars(play=play)
                        play_context = PlayContext(play=play,
                                                   options=self.options)
                        for block in play.compile():
                            block = block.filter_tagged_tasks(
                                play_context, all_vars)
                            if not block.has_tasks():
                                continue
                            taskmsg += _process_block(block)

                        if self.options.listtags:
                            cur_tags = list(mytags.union(all_tags))
                            cur_tags.sort()
                            taskmsg += "      TASK TAGS: [%s]\n" % ', '.join(
                                cur_tags)

                        display.display(taskmsg)

            return 0
        else:
            return results
Example #33
0
    def install(self):

        if self.scm:
            # create tar file from scm url
            tmp_file = RoleRequirement.scm_archive_role(
                keep_scm_meta=self.options.keep_scm_meta, **self.spec)
        elif self.src:
            if os.path.isfile(self.src):
                tmp_file = self.src
            elif '://' in self.src:
                role_data = self.src
                tmp_file = self.fetch(role_data)
            else:
                api = GalaxyAPI(self.galaxy)
                role_data = api.lookup_role_by_name(self.src)
                if not role_data:
                    raise AnsibleError("- sorry, %s was not found on %s." %
                                       (self.src, api.api_server))

                if role_data.get('role_type') == 'APP':
                    # Container Role
                    display.warning(
                        "%s is a Container App role, and should only be installed using Ansible "
                        "Container" % self.name)

                role_versions = api.fetch_role_related('versions',
                                                       role_data['id'])
                if not self.version:
                    # convert the version names to LooseVersion objects
                    # and sort them to get the latest version. If there
                    # are no versions in the list, we'll grab the head
                    # of the master branch
                    if len(role_versions) > 0:
                        loose_versions = [
                            LooseVersion(a.get('name', None))
                            for a in role_versions
                        ]
                        try:
                            loose_versions.sort()
                        except TypeError:
                            raise AnsibleError(
                                'Unable to compare role versions (%s) to determine the most recent version due to incompatible version formats. '
                                'Please contact the role author to resolve versioning conflicts, or specify an explicit role version to '
                                'install.' %
                                ', '.join([v.vstring for v in loose_versions]))
                        self.version = str(loose_versions[-1])
                    elif role_data.get('github_branch', None):
                        self.version = role_data['github_branch']
                    else:
                        self.version = 'master'
                elif self.version != 'master':
                    if role_versions and str(self.version) not in [
                            a.get('name', None) for a in role_versions
                    ]:
                        raise AnsibleError(
                            "- the specified version (%s) of %s was not found in the list of available versions (%s)."
                            % (self.version, self.name, role_versions))

                # check if there's a source link for our role_version
                for role_version in role_versions:
                    if role_version[
                            'name'] == self.version and 'source' in role_version:
                        self.src = role_version['source']

                tmp_file = self.fetch(role_data)

        else:
            raise AnsibleError("No valid role data found")

        if tmp_file:

            display.debug("installing from %s" % tmp_file)

            if not tarfile.is_tarfile(tmp_file):
                raise AnsibleError("the file downloaded was not a tar.gz")
            else:
                if tmp_file.endswith('.gz'):
                    role_tar_file = tarfile.open(tmp_file, "r:gz")
                else:
                    role_tar_file = tarfile.open(tmp_file, "r")
                # verify the role's meta file
                meta_file = None
                members = role_tar_file.getmembers()
                # next find the metadata file
                for member in members:
                    for meta_main in self.META_MAIN:
                        if meta_main in member.name:
                            # Look for parent of meta/main.yml
                            # Due to possibility of sub roles each containing meta/main.yml
                            # look for shortest length parent
                            meta_parent_dir = os.path.dirname(
                                os.path.dirname(member.name))
                            if not meta_file:
                                archive_parent_dir = meta_parent_dir
                                meta_file = member
                            else:
                                if len(meta_parent_dir) < len(
                                        archive_parent_dir):
                                    archive_parent_dir = meta_parent_dir
                                    meta_file = member
                if not meta_file:
                    raise AnsibleError(
                        "this role does not appear to have a meta/main.yml file."
                    )
                else:
                    try:
                        self._metadata = yaml.safe_load(
                            role_tar_file.extractfile(meta_file))
                    except:
                        raise AnsibleError(
                            "this role does not appear to have a valid meta/main.yml file."
                        )

                # we strip off any higher-level directories for all of the files contained within
                # the tar file here. The default is 'github_repo-target'. Gerrit instances, on the other
                # hand, does not have a parent directory at all.
                installed = False
                while not installed:
                    display.display("- extracting %s to %s" %
                                    (self.name, self.path))
                    try:
                        if os.path.exists(self.path):
                            if not os.path.isdir(self.path):
                                raise AnsibleError(
                                    "the specified roles path exists and is not a directory."
                                )
                            elif not getattr(self.options, "force", False):
                                raise AnsibleError(
                                    "the specified role %s appears to already exist. Use --force to replace it."
                                    % self.name)
                            else:
                                # using --force, remove the old path
                                if not self.remove():
                                    raise AnsibleError(
                                        "%s doesn't appear to contain a role.\n  please remove this directory manually if you really "
                                        "want to put the role here." %
                                        self.path)
                        else:
                            os.makedirs(self.path)

                        # now we do the actual extraction to the path
                        for member in members:
                            # we only extract files, and remove any relative path
                            # bits that might be in the file for security purposes
                            # and drop any containing directory, as mentioned above
                            if member.isreg() or member.issym():
                                parts = member.name.replace(
                                    archive_parent_dir, "", 1).split(os.sep)
                                final_parts = []
                                for part in parts:
                                    if part != '..' and '~' not in part and '$' not in part:
                                        final_parts.append(part)
                                member.name = os.path.join(*final_parts)
                                role_tar_file.extract(member, self.path)

                        # write out the install info file for later use
                        self._write_galaxy_install_info()
                        installed = True
                    except OSError as e:
                        error = True
                        if e.errno == errno.EACCES and len(self.paths) > 1:
                            current = self.paths.index(self.path)
                            if len(self.paths) > current:
                                self.path = self.paths[current + 1]
                                error = False
                        if error:
                            raise AnsibleError(
                                "Could not update files in %s: %s" %
                                (self.path, str(e)))

                # return the parsed yaml metadata
                display.display("- %s was installed successfully" % str(self))
                if not (self.src and os.path.isfile(self.src)):
                    try:
                        os.unlink(tmp_file)
                    except (OSError, IOError) as e:
                        display.warning("Unable to remove tmp file (%s): %s" %
                                        (tmp_file, str(e)))
                return True

        return False
Example #34
0
    def _lookup(self, name, *args, **kwargs):
        instance = self._lookup_loader.get(name.lower(),
                                           loader=self._loader,
                                           templar=self)

        if instance is not None:
            wantlist = kwargs.pop('wantlist', False)
            allow_unsafe = kwargs.pop('allow_unsafe',
                                      C.DEFAULT_ALLOW_UNSAFE_LOOKUPS)
            errors = kwargs.pop('errors', 'strict')

            from ansible.utils.listify import listify_lookup_plugin_terms
            loop_terms = listify_lookup_plugin_terms(terms=args,
                                                     templar=self,
                                                     loader=self._loader,
                                                     fail_on_undefined=True,
                                                     convert_bare=False)
            # safely catch run failures per #5059
            try:
                ran = instance.run(loop_terms,
                                   variables=self._available_variables,
                                   **kwargs)
            except (AnsibleUndefinedVariable, UndefinedError) as e:
                raise AnsibleUndefinedVariable(e)
            except Exception as e:
                if self._fail_on_lookup_errors:
                    msg = u"An unhandled exception occurred while running the lookup plugin '%s'. Error was a %s, original message: %s" % \
                          (name, type(e), to_text(e))
                    if errors == 'warn':
                        display.warning(msg)
                    elif errors == 'ignore':
                        display.display(msg, log_only=True)
                    else:
                        raise AnsibleError(to_native(msg))
                ran = None

            if ran and not allow_unsafe:
                if wantlist:
                    ran = wrap_var(ran)
                else:
                    try:
                        ran = UnsafeProxy(",".join(ran))
                    except TypeError:
                        # Lookup Plugins should always return lists.  Throw an error if that's not
                        # the case:
                        if not isinstance(ran, Sequence):
                            raise AnsibleError(
                                "The lookup plugin '%s' did not return a list."
                                % name)

                        # The TypeError we can recover from is when the value *inside* of the list
                        # is not a string
                        if len(ran) == 1:
                            ran = wrap_var(ran[0])
                        else:
                            ran = wrap_var(ran)

                if self.cur_context:
                    self.cur_context.unsafe = True
            return ran
        else:
            raise AnsibleError("lookup plugin (%s) not found" % name)
Example #35
0
    def _start_connection(self, variables):
        '''
        Starts the persistent connection
        '''
        master, slave = pty.openpty()

        python = sys.executable

        def find_file_in_path(filename):
            # Check $PATH first, followed by same directory as sys.argv[0]
            paths = os.environ['PATH'].split(
                os.pathsep) + [os.path.dirname(sys.argv[0])]
            for dirname in paths:
                fullpath = os.path.join(dirname, filename)
                if os.path.isfile(fullpath):
                    return fullpath

            raise AnsibleError("Unable to find location of '%s'" % filename)

        p = subprocess.Popen([
            python,
            find_file_in_path('ansible-connection'),
            to_text(os.getppid())
        ],
                             stdin=slave,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        stdin = os.fdopen(master, 'wb', 0)
        os.close(slave)

        # Need to force a protocol that is compatible with both py2 and py3.
        # That would be protocol=2 or less.
        # Also need to force a protocol that excludes certain control chars as
        # stdin in this case is a pty and control chars will cause problems.
        # that means only protocol=0 will work.
        src = cPickle.dumps(self._play_context.serialize(), protocol=0)
        stdin.write(src)
        stdin.write(b'\n#END_INIT#\n')

        src = cPickle.dumps(variables, protocol=0)
        stdin.write(src)
        stdin.write(b'\n#END_VARS#\n')

        stdin.flush()

        (stdout, stderr) = p.communicate()
        stdin.close()

        if p.returncode == 0:
            result = json.loads(
                to_text(stdout, errors='surrogate_then_replace'))
        else:
            try:
                result = json.loads(
                    to_text(stderr, errors='surrogate_then_replace'))
            except getattr(json.decoder, 'JSONDecodeError', ValueError):
                # JSONDecodeError only available on Python 3.5+
                result = {
                    'error': to_text(stderr, errors='surrogate_then_replace')
                }

        if 'messages' in result:
            for msg in result.get('messages'):
                display.vvvv('%s' % msg, host=self._play_context.remote_addr)

        if 'error' in result:
            if self._play_context.verbosity > 2:
                if result.get('exception'):
                    msg = "The full traceback is:\n" + result['exception']
                    display.display(msg, color=C.COLOR_ERROR)
            raise AnsibleError(result['error'])

        return result['socket_path']
Example #36
0
    def _connect(self):
        super(Connection, self)._connect()

        display.display('ssh connection done, stating ncclient', log_only=True)

        self.allow_agent = True
        if self._play_context.password is not None:
            self.allow_agent = False

        self.key_filename = None
        if self._play_context.private_key_file:
            self.key_filename = os.path.expanduser(
                self._play_context.private_key_file)

        network_os = self._play_context.network_os

        if not network_os:
            for cls in netconf_loader.all(class_only=True):
                network_os = cls.guess_network_os(self)
                if network_os:
                    display.display('discovered network_os %s' % network_os,
                                    log_only=True)

        if not network_os:
            raise AnsibleConnectionFailure(
                'Unable to automatically determine host network os. Please ansible_network_os value'
            )

        try:
            self._manager = manager.connect(
                host=self._play_context.remote_addr,
                port=self._play_context.port or 830,
                username=self._play_context.remote_user,
                password=self._play_context.password,
                key_filename=str(self.key_filename),
                hostkey_verify=C.HOST_KEY_CHECKING,
                look_for_keys=C.PARAMIKO_LOOK_FOR_KEYS,
                allow_agent=self.allow_agent,
                timeout=self._play_context.timeout,
                device_params={'name': network_os})
        except SSHUnknownHostError as exc:
            raise AnsibleConnectionFailure(str(exc))

        if not self._manager.connected:
            return 1, b'', b'not connected'

        display.display('ncclient manager object created successfully',
                        log_only=True)

        self._connected = True

        self._netconf = netconf_loader.get(network_os, self)
        if self._netconf:
            self._rpc.add(self._netconf)
            display.display('loaded netconf plugin for network_os %s' %
                            network_os,
                            log_only=True)
        else:
            display.display('unable to load netconf for network_os %s' %
                            network_os)

        return 0, to_bytes(self._manager.session_id,
                           errors='surrogate_or_strict'), b''
Example #37
0
    def _connect(self):
        if not HAS_NCCLIENT:
            raise AnsibleError(
                'ncclient is required to use the netconf connection type.\n'
                'Please run pip install ncclient')

        super(Connection, self)._connect()
        display.display('ssh connection done, starting ncclient',
                        log_only=True)

        allow_agent = True
        if self._play_context.password is not None:
            allow_agent = False
        setattr(self._play_context, 'allow_agent', allow_agent)

        self.key_filename = self._play_context.private_key_file or self.get_option(
            'private_key_file')
        if self.key_filename:
            self.key_filename = str(os.path.expanduser(self.key_filename))

        if self._network_os == 'default':
            for cls in netconf_loader.all(class_only=True):
                network_os = cls.guess_network_os(self)
                if network_os:
                    display.display('discovered network_os %s' % network_os,
                                    log_only=True)
                    self._network_os = network_os

        device_params = {
            'name':
            NETWORK_OS_DEVICE_PARAM_MAP.get(self._network_os)
            or self._network_os
        }

        ssh_config = self.get_option('netconf_ssh_config')
        if ssh_config in BOOLEANS_TRUE:
            ssh_config = True
        elif ssh_config in BOOLEANS_FALSE:
            ssh_config = None

        try:
            port = self._play_context.port or 830
            display.vvvv(
                "ESTABLISH NETCONF SSH CONNECTION FOR USER: %s on PORT %s TO %s"
                % (self._play_context.remote_user, port,
                   self._play_context.remote_addr))
            self._manager = manager.connect(
                host=self._play_context.remote_addr,
                port=port,
                username=self._play_context.remote_user,
                password=self._play_context.password,
                key_filename=self.key_filename,
                hostkey_verify=self.get_option('host_key_checking'),
                look_for_keys=self.get_option('look_for_keys'),
                device_params=device_params,
                allow_agent=self._play_context.allow_agent,
                timeout=self._play_context.timeout,
                ssh_config=ssh_config)
        except SSHUnknownHostError as exc:
            raise AnsibleConnectionFailure(str(exc))
        except ImportError as exc:
            raise AnsibleError(
                "connection=netconf is not supported on {0}".format(
                    self._network_os))

        if not self._manager.connected:
            return 1, b'', b'not connected'

        display.display('ncclient manager object created successfully',
                        log_only=True)

        self._connected = True

        netconf = netconf_loader.get(self._network_os, self)
        if netconf:
            display.display('loaded netconf plugin for network_os %s' %
                            self._network_os,
                            log_only=True)
        else:
            netconf = netconf_loader.get("default", self)
            display.display(
                'unable to load netconf plugin for network_os %s, falling back to default plugin'
                % self._network_os)
        self._implementation_plugins.append(netconf)

        super(Connection, self)._connect()

        return 0, to_bytes(self._manager.session_id,
                           errors='surrogate_or_strict'), b''
Example #38
0
    def run(self):
        ''' use Runner lib to do SSH things '''

        super(PullCLI, self).run()

        # log command line
        now = datetime.datetime.now()
        display.display(now.strftime("Starting Ansible Pull at %F %T"))
        display.display(' '.join(sys.argv))

        # Build Checkout command
        # Now construct the ansible command
        node = platform.node()
        host = socket.getfqdn()
        limit_opts = 'localhost,%s,127.0.0.1' % ','.join(
            set([host, node,
                 host.split('.')[0],
                 node.split('.')[0]]))
        base_opts = '-c local '
        if self.options.verbosity > 0:
            base_opts += ' -%s' % ''.join(
                ["v" for x in range(0, self.options.verbosity)])

        # Attempt to use the inventory passed in as an argument
        # It might not yet have been downloaded so use localhost if note
        if not self.options.inventory or not os.path.exists(
                self.options.inventory):
            inv_opts = 'localhost,'
        else:
            inv_opts = self.options.inventory

        #FIXME: enable more repo modules hg/svn?
        if self.options.module_name == 'git':
            repo_opts = "name=%s dest=%s" % (self.options.url,
                                             self.options.dest)
            if self.options.checkout:
                repo_opts += ' version=%s' % self.options.checkout

            if self.options.accept_host_key:
                repo_opts += ' accept_hostkey=yes'

            if self.options.private_key_file:
                repo_opts += ' key_file=%s' % self.options.private_key_file

            if self.options.verify:
                repo_opts += ' verify_commit=yes'

        path = module_loader.find_plugin(self.options.module_name)
        if path is None:
            raise AnsibleOptionsError(
                ("module '%s' not found.\n" % self.options.module_name))

        bin_path = os.path.dirname(os.path.abspath(sys.argv[0]))
        cmd = '%s/ansible -i "%s" %s -m %s -a "%s" "%s"' % (
            bin_path, inv_opts, base_opts, self.options.module_name, repo_opts,
            limit_opts)

        for ev in self.options.extra_vars:
            cmd += ' -e "%s"' % ev

        # Nap?
        if self.options.sleep:
            display.display("Sleeping for %d seconds..." % self.options.sleep)
            time.sleep(self.options.sleep)

        # RUN the Checkout command
        display.debug("running ansible with VCS module to checkout repo")
        display.vvvv('EXEC: %s' % cmd)
        rc, out, err = run_cmd(cmd, live=True)

        if rc != 0:
            if self.options.force:
                display.warning(
                    "Unable to update repository. Continuing with (forced) run of playbook."
                )
            else:
                return rc
        elif self.options.ifchanged and '"changed": true' not in out:
            display.display("Repository has not changed, quitting.")
            return 0

        playbook = self.select_playbook(self.options.dest)
        if playbook is None:
            raise AnsibleOptionsError("Could not find a playbook to run.")

        # Build playbook command
        cmd = '%s/ansible-playbook %s %s' % (bin_path, base_opts, playbook)
        if self.options.vault_password_file:
            cmd += " --vault-password-file=%s" % self.options.vault_password_file
        if self.options.inventory:
            cmd += ' -i "%s"' % self.options.inventory
        for ev in self.options.extra_vars:
            cmd += ' -e "%s"' % ev
        if self.options.ask_sudo_pass or self.options.ask_su_pass or self.options.become_ask_pass:
            cmd += ' --ask-become-pass'
        if self.options.tags:
            cmd += ' -t "%s"' % self.options.tags
        if self.options.subset:
            cmd += ' -l "%s"' % self.options.subset

        os.chdir(self.options.dest)

        # RUN THE PLAYBOOK COMMAND
        display.debug("running ansible-playbook to do actual work")
        display.debug('EXEC: %s' % cmd)
        rc, out, err = run_cmd(cmd, live=True)

        if self.options.purge:
            os.chdir('/')
            try:
                shutil.rmtree(self.options.dest)
            except Exception as e:
                display.error("Failed to remove %s: %s" %
                              (self.options.dest, str(e)))

        return rc