class TestDataLoaderWithVault(unittest.TestCase): def setUp(self): self._loader = DataLoader() self._loader.set_vault_password('ansible') def tearDown(self): pass @patch.multiple(DataLoader, path_exists=lambda s, x: True, is_file=lambda s, x: True) def test_parse_from_vault_1_1_file(self): vaulted_data = """$ANSIBLE_VAULT;1.1;AES256 33343734386261666161626433386662623039356366656637303939306563376130623138626165 6436333766346533353463636566313332623130383662340a393835656134633665333861393331 37666233346464636263636530626332623035633135363732623332313534306438393366323966 3135306561356164310a343937653834643433343734653137383339323330626437313562306630 3035 """ if PY3: builtins_name = 'builtins' else: builtins_name = '__builtin__' with patch(builtins_name + '.open', mock_open(read_data=vaulted_data.encode('utf-8'))): output = self._loader.load_from_file('dummy_vault.txt') self.assertEqual(output, dict(foo='bar'))
class TestDataLoaderWithVault(unittest.TestCase): def setUp(self): self._loader = DataLoader() self._loader.set_vault_password('ansible') def tearDown(self): pass @patch.multiple(DataLoader, path_exists=lambda s, x: True, is_file=lambda s, x: True) def test_parse_from_vault_1_1_file(self): vaulted_data = """$ANSIBLE_VAULT;1.1;AES256 33343734386261666161626433386662623039356366656637303939306563376130623138626165 6436333766346533353463636566313332623130383662340a393835656134633665333861393331 37666233346464636263636530626332623035633135363732623332313534306438393366323966 3135306561356164310a343937653834643433343734653137383339323330626437313562306630 3035 """ if PY3: builtins_name = 'builtins' else: builtins_name = '__builtin__' with patch(builtins_name + '.open', mock_open(read_data=vaulted_data.encode('utf-8'))): output = self._loader.load_from_file('dummy_vault.txt') self.assertEqual(output, dict(foo='bar'))
def _play_prereqs(options): # all needs loader loader = DataLoader() # vault b_vault_pass = None if options.vault_password_file: # read vault_pass from a file b_vault_pass = CLI.read_vault_password_file( options.vault_password_file, loader=loader) elif options.ask_vault_pass: b_vault_pass = CLI.ask_vault_passwords() if b_vault_pass is not None: loader.set_vault_password(b_vault_pass) # create the inventory, and filter it based on the subset specified (if any) inventory = InventoryManager(loader=loader, sources=options.inventory) # create the variable manager, which will be shared throughout # the code, ensuring a consistent view of global variables variable_manager = VariableManager(loader=loader, inventory=inventory) # load vars from cli options variable_manager.extra_vars = load_extra_vars(loader=loader, options=options) variable_manager.options_vars = load_options_vars( options, CLI.version_info(gitinfo=False)) return loader, inventory, variable_manager
def create_ansible_objects(inventory_file, extra_vars, forks=50): """Create the default ansible objects needed to run a playbook. :param inventory_file: The path to the inventory file :param extra_vars: The dictionary containing the collection status of the optional products. :param forks: number of forks to run with, default of 50 :returns: tuple of (options, variable_manager, loader, inventory) """ named_options = namedtuple('Options', [ 'connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check' ]) options = named_options(connection='ssh', module_path=C.DEFAULT_MODULE_PATH, forks=forks, become=False, become_method='sudo', become_user='******', check=False) variable_manager = VariableManager() loader = DataLoader() loader.set_vault_password(settings.SECRET_KEY) # create inventory and extra vars and pass to var manager inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list=inventory_file) variable_manager.extra_vars = extra_vars variable_manager.set_inventory(inventory) return options, variable_manager, loader, inventory
def get_play_prereqs_2(self, options): loader = DataLoader() if self.vault_pass: loader.set_vault_password(self.vault_pass) variable_manager = VariableManager() variable_manager.extra_vars = self.extra_vars variable_manager.options_vars = { 'ansible_version': self.version_info(ansible_version) } # Add this to avoid the Ansible bug: no host vars as host is not in inventory # In version 2.0.1 it must be fixed ansible.inventory.HOSTS_PATTERNS_CACHE = {} inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list=options.inventory) variable_manager.set_inventory(inventory) if self.host: inventory.subset(self.host) # let inventory know which playbooks are using so it can know the # basedirs inventory.set_playbook_basedir(os.path.dirname(self.playbook_file)) return loader, inventory, variable_manager
class Runner(object): def __init__(self, playbook, display, hosts=None, options={}, passwords={}, vault_pass=None): self.options = Options() for k, v in options.iteritems(): setattr(self.options, k, v) self.display = display self.display.verbosity = self.options.verbosity # executor has its own verbosity setting playbook_executor.verbosity = self.options.verbosity # gets data from YAML/JSON files self.loader = DataLoader() if vault_pass is not None: self.loader.set_vault_password(vault_pass) elif 'VAULT_PASS' in os.environ: self.loader.set_vault_password(os.environ['VAULT_PASS']) # all the variables from all the various places self.variable_manager = VariableManager() if self.options.python_interpreter is not None: self.variable_manager.extra_vars = { 'ansible_python_interpreter': self.options.python_interpreter } # set inventory, using most of above objects self.inventory = Inventory( loader=self.loader, variable_manager=self.variable_manager, host_list=hosts) if len(self.inventory.list_hosts()) == 0: self.display.error("Provided hosts list is empty.") sys.exit(1) self.inventory.subset(self.options.subset) if len(self.inventory.list_hosts()) == 0: self.display.error("Specified limit does not match any hosts.") sys.exit(1) self.variable_manager.set_inventory(self.inventory) # setup playbook executor, but don't run until run() called self.pbex = playbook_executor.PlaybookExecutor( playbooks=[playbook], inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=passwords) def run(self): # run playbook and get stats self.pbex.run() stats = self.pbex._tqm._stats return stats
class Runner(object): def __init__(self, playbook, display, hosts=None, options={}, passwords={}, vault_pass=None): self.options = Options() for k, v in options.iteritems(): setattr(self.options, k, v) self.display = display self.display.verbosity = self.options.verbosity # executor has its own verbosity setting playbook_executor.verbosity = self.options.verbosity # gets data from YAML/JSON files self.loader = DataLoader() if vault_pass is not None: self.loader.set_vault_password(vault_pass) elif 'VAULT_PASS' in os.environ: self.loader.set_vault_password(os.environ['VAULT_PASS']) # all the variables from all the various places self.variable_manager = VariableManager() if self.options.python_interpreter is not None: self.variable_manager.extra_vars = { 'ansible_python_interpreter': self.options.python_interpreter } # set inventory, using most of above objects self.inventory = Inventory( loader=self.loader, variable_manager=self.variable_manager, host_list=hosts) if len(self.inventory.list_hosts()) == 0: self.display.error("Provided hosts list is empty.") sys.exit(1) self.inventory.subset(self.options.subset) if len(self.inventory.list_hosts()) == 0: self.display.error("Specified limit does not match any hosts.") sys.exit(1) self.variable_manager.set_inventory(self.inventory) # setup playbook executor, but don't run until run() called self.pbex = playbook_executor.PlaybookExecutor( playbooks=[playbook], inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=passwords) def run(self): # run playbook and get stats self.pbex.run() stats = self.pbex._tqm._stats return stats
class Runner(object): def __init__(self, hostnames, playbook, private_key_file, run_data, become_pass=None, verbosity=0, callback=None, subset_pattern=None): self.hostnames = hostnames self.playbook = os.path.join(playbooks_dir, playbook) self.run_data = run_data self.options = Options(subset=subset_pattern, private_key_file=private_key_file, verbosity=verbosity) self.display = Display() self.display.verbosity = verbosity playbook_executor.verbosity = verbosity passwords = {'become_pass': None} # Gets data from YAML/JSON files self.loader = DataLoader() self.loader.set_vault_password(os.environ.get('VAULT_PASS')) self.variable_manager = VariableManager() self.variable_manager.extra_vars = self.run_data self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=self.hostnames) self.variable_manager.set_inventory(self.inventory) self.pbex = playbook_executor.PlaybookExecutor( playbooks=[self.playbook], inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=passwords) if callback: self.pbex._tqm._stdout_callback = callback def run(self): self.pbex.run() stats = self.pbex._tqm._stats run_success = True hosts = sorted(stats.processed.keys()) for h in hosts: t = stats.summarize(h) if t['unreachable'] > 0 or t['failures'] > 0: run_success = False return run_success
def _load_auth_secrets(self, hosts=[], secrets=[]): for _safe, _lockpick in secrets: try: _safe_loader = DataLoader(); _safe_lockpick = CLI.read_vault_password_file(_lockpick, loader=_safe_loader); _safe_loader.set_vault_password(_safe_lockpick); _safe_contents = _safe_loader.load_from_file(_safe); if 'credentials' not in _safe_contents: return dict(); #display.display(pprint.pformat(_safe_contents, indent=4), color='green'); return _safe_contents['credentials']; except Exception as e: display.display('[ERROR] ' + str(e), color='red'); return dict();
def get_host_vars(self, host, vault_password=None): """ Get host specific variables. """ resulting_host_vars = {} var_files = [] for host_var_folder in self.host_vars_folders: var_files.extend(vars_files_loading(host_var_folder, host.name)) _dataloader = DataLoader() _dataloader.set_vault_password(vault_password) for filename in var_files: display.vvvvv("Hostname {}: Loading var file {}".format( host.name, filename)) data = _dataloader.load_from_file(filename) if data is not None: resulting_host_vars = merge_hash(resulting_host_vars, data) return resulting_host_vars
def role_vars(): parser.add_argument( '--playbook_path', help='Test Playbook path for role - usually role/tests/test.yml', required=True) parser.add_argument( '--vault_password_file', help='Vault password file - usually role/tests/test.yml') args = parser.parse_args() variable_manager = VariableManager() loader = DataLoader() if args.vault_password_file: # read vault_pass from a file vault_pass = CLI.read_vault_password_file(args.vault_password_file, loader=loader) loader.set_vault_password(vault_pass) playbook_path = args.playbook_path if not os.path.exists(playbook_path): print('[ERROR] The playbook does not exist') sys.exit(1) pb = Playbook.load(playbook_path, variable_manager=variable_manager, loader=loader) plays = pb.get_plays() if len(plays) == 0: print('[ERROR] No plays in playbook') sys.exit(1) first_play = plays[0] if first_play._included_path is not None: loader.set_basedir(first_play._included_path) else: loader.set_basedir(pb._basedir) result = dict() for role in first_play.roles: result.update(role.get_default_vars()) result.update(role.get_vars()) print(json.dumps(result))
def get_group_vars(self, group, vault_password=None): """ Get group specific variables. """ resulting_group_vars = {} var_files = [] for grp_var_folder in self.grp_vars_folders: var_files.extend(vars_files_loading(grp_var_folder, group.name)) _dataloader = DataLoader() _dataloader.set_vault_password(vault_password) for filename in var_files: display.vvvvv("Group {}: Loading var file {}".format( group.name, filename)) data = _dataloader.load_from_file(filename) if data is not None: resulting_group_vars = merge_hash(resulting_group_vars, data) return resulting_group_vars
class FrecklesRunner(object): def __init__(self, playbook, become_pass, verbosity=0): self.options = Options() self.options.verbosity = verbosity self.display = Display() self.display.verbosity = self.options.verbosity playbook_executor.verbosity = self.options.verbosity passwords = {'become_pass': become_pass} self.loader = DataLoader() self.loader.set_vault_password(os.environ['VAULT_PASS']) self.variable_manager = VariableManager() self.variable_manager.extra_vars = self.run_data
def __init__(self, inventory, ask_vault_pass, vault_password_files, vault_ids): if vault_ids or len(vault_password_files) > 1: raise NotImplementedError from ansible.cli import CLI super(Inventory20, self).__init__() loader = DataLoader() if ask_vault_pass: self.vault_pass = CLI.ask_vault_passwords() elif vault_password_files: self.vault_pass = CLI.read_vault_password_file(vault_password_files[0], loader) if self.vault_pass is not None: loader.set_vault_password(self.vault_pass) self.variable_manager = VariableManager() try: self.inventory = ansible.inventory.Inventory(loader=loader, variable_manager=self.variable_manager, host_list=inventory) except ansible.errors.AnsibleError: raise NoVaultSecretFound self.variable_manager.set_inventory(self.inventory)
def get_play_prereqs_2(self, options): loader = DataLoader() if self.vault_pass: loader.set_vault_password(self.vault_pass) variable_manager = VariableManager() variable_manager.extra_vars = self.extra_vars variable_manager.options_vars = { 'ansible_version': self.version_info(ansible_version) } inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list=options.inventory) variable_manager.set_inventory(inventory) # let inventory know which playbooks are using so it can know the # basedirs inventory.set_playbook_basedir(os.path.dirname(self.playbook_file)) return loader, inventory, variable_manager
def __init__(self, inventory, ask_vault_pass, vault_password_files, vault_ids): if vault_ids or len(vault_password_files) > 1: raise NotImplementedError from ansible.cli import CLI super(Inventory20, self).__init__() loader = DataLoader() if ask_vault_pass: self.vault_pass = CLI.ask_vault_passwords() elif vault_password_files: self.vault_pass = CLI.read_vault_password_file( vault_password_files[0], loader) if self.vault_pass is not None: loader.set_vault_password(self.vault_pass) self.variable_manager = VariableManager() try: self.inventory = ansible.inventory.Inventory( loader=loader, variable_manager=self.variable_manager, host_list=inventory) except ansible.errors.AnsibleError: raise NoVaultSecretFound self.variable_manager.set_inventory(self.inventory)
def setup_inventory(ctx): """sets up the inventory object for use by other functions""" loader = DataLoader() variable_manager = VariableManager() if ctx.obj['vault_password_command']: try: vault_password = subprocess.check_output( ctx.obj['vault_password_command'], shell=True) except subprocess.CalledProcessError: print "Vault password command exited with non-zero code" sys.exit(1) loader.set_vault_password(vault_password.rstrip()) elif os.environ.get('ANSIBLE_VAULT_PASSWORD_FILE'): with open(os.environ.get('ANSIBLE_VAULT_PASSWORD_FILE'), 'r') as vault_password_file: loader.set_vault_password(vault_password_file.read().strip()) try: if (ctx.obj['inventory']): return Inventory(loader=loader, variable_manager=variable_manager, host_list=ctx.obj['inventory']) else: return Inventory(loader=loader, variable_manager=variable_manager) except AnsibleError as e: # If it fails to decrypt and a password command hasn't been provided, prompt for one if not ctx.obj['vault_password_command'] and str( e) == 'Decryption failed': vault_password = getpass.getpass('Enter vault password:'******'inventory']): return Inventory(loader=loader, variable_manager=variable_manager, host_list=ctx.obj['inventory']) else: return Inventory(loader=loader, variable_manager=variable_manager) else: print "Something went wrong:" print repr(e) sys.exit(1)
def setup_inventory(ctx): """sets up the inventory object for use by other functions""" loader = DataLoader() variable_manager = VariableManager() if ctx.obj['vault_password_command']: try: vault_password = subprocess.check_output(ctx.obj['vault_password_command'], shell=True) except subprocess.CalledProcessError: print "Vault password command exited with non-zero code" sys.exit(1) loader.set_vault_password(vault_password.rstrip()) elif os.environ.get('ANSIBLE_VAULT_PASSWORD_FILE'): with open(os.environ.get('ANSIBLE_VAULT_PASSWORD_FILE'), 'r') as vault_password_file: loader.set_vault_password(vault_password_file.read().strip()) try: if(ctx.obj['inventory']): return Inventory(loader=loader, variable_manager=variable_manager, host_list=ctx.obj['inventory']) else: return Inventory(loader=loader, variable_manager=variable_manager) except AnsibleError as e: # If it fails to decrypt and a password command hasn't been provided, prompt for one if not ctx.obj['vault_password_command'] and str(e) == 'Decryption failed': vault_password = getpass.getpass('Enter vault password:'******'inventory']): return Inventory(loader=loader, variable_manager=variable_manager, host_list=ctx.obj['inventory']) else: return Inventory(loader=loader, variable_manager=variable_manager) else: print "Something went wrong:" print repr(e) sys.exit(1)
class InventoryCLI(CLI): ''' used to display or dump the configured inventory as Ansible sees it ''' ARGUMENTS = { 'host': 'The name of a host to match in the inventory, relevant when using --list', 'group': 'The name of a group in the inventory, relevant when using --graph', } def __init__(self, args): super(InventoryCLI, self).__init__(args) self.vm = None self.loader = None self.inventory = None self._new_api = True def parse(self): self.parser = CLI.base_parser( usage='usage: %prog [options] [host|group]', epilog= 'Show Ansible inventory information, by default it uses the inventory script JSON format', inventory_opts=True, vault_opts=True, basedir_opts=True, ) # remove unused default options self.parser.remove_option('--limit') self.parser.remove_option('--list-hosts') # Actions action_group = optparse.OptionGroup( self.parser, "Actions", "One of following must be used on invocation, ONLY ONE!") action_group.add_option( "--list", action="store_true", default=False, dest='list', help='Output all hosts info, works as inventory script') action_group.add_option( "--host", action="store", default=None, dest='host', help='Output specific host info, works as inventory script') action_group.add_option( "--graph", action="store_true", default=False, dest='graph', help= 'create inventory graph, if supplying pattern it must be a valid group name' ) self.parser.add_option_group(action_group) # graph self.parser.add_option( "-y", "--yaml", action="store_true", default=False, dest='yaml', help='Use YAML format instead of default JSON, ignored for --graph' ) self.parser.add_option( "--vars", action="store_true", default=False, dest='show_vars', help='Add vars to graph display, ignored unless used with --graph') # list self.parser.add_option( "--export", action="store_true", default=C.INVENTORY_EXPORT, dest='export', help= "When doing an --list, represent in a way that is optimized for export," "not as an accurate representation of how Ansible has processed it" ) # self.parser.add_option("--ignore-vars-plugins", action="store_true", default=False, dest='ignore_vars_plugins', # help="When doing an --list, skip vars data from vars plugins, by default, this would include group_vars/ and host_vars/") super(InventoryCLI, self).parse() display.verbosity = self.options.verbosity self.validate_conflicts(vault_opts=True) # there can be only one! and, at least, one! used = 0 for opt in (self.options.list, self.options.host, self.options.graph): if opt: used += 1 if used == 0: raise AnsibleOptionsError( "No action selected, at least one of --host, --graph or --list needs to be specified." ) elif used > 1: raise AnsibleOptionsError( "Conflicting options used, only one of --host, --graph or --list can be used at the same time." ) # set host pattern to default if not supplied if len(self.args) > 0: self.options.pattern = self.args[0] else: self.options.pattern = 'all' 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) def dump(self, stuff): if self.options.yaml: import yaml from ansible.parsing.yaml.dumper import AnsibleDumper results = yaml.dump(stuff, Dumper=AnsibleDumper, default_flow_style=False) else: import json from ansible.parsing.ajson import AnsibleJSONEncoder results = json.dumps(stuff, cls=AnsibleJSONEncoder, sort_keys=True, indent=4) return results # FIXME: refactor to use same for VM def get_plugin_vars(self, path, entity): data = {} def _get_plugin_vars(plugin, path, entities): data = {} try: data = plugin.get_vars(self.loader, path, entity) except AttributeError: try: if isinstance(entity, Host): data = combine_vars(data, plugin.get_host_vars(entity.name)) else: data = combine_vars(data, plugin.get_group_vars(entity.name)) except AttributeError: if hasattr(plugin, 'run'): raise AnsibleError( "Cannot use v1 type vars plugin %s from %s" % (plugin._load_name, plugin._original_path)) else: raise AnsibleError( "Invalid vars plugin %s from %s" % (plugin._load_name, plugin._original_path)) return data for plugin in vars_loader.all(): data = combine_vars(data, _get_plugin_vars(plugin, path, entity)) return data def _get_group_variables(self, group): # get info from inventory source res = group.get_vars() # FIXME: add switch to skip vars plugins, add vars plugin info for inventory_dir in self.inventory._sources: res = combine_vars(res, self.get_plugin_vars(inventory_dir, group)) if group.priority != 1: res['ansible_group_priority'] = group.priority return res def _get_host_variables(self, host): if self.options.export: hostvars = host.get_vars() # FIXME: add switch to skip vars plugins # add vars plugin info for inventory_dir in self.inventory._sources: hostvars = combine_vars( hostvars, self.get_plugin_vars(inventory_dir, host)) else: if self._new_api: hostvars = self.vm.get_vars(host=host, include_hostvars=False) else: hostvars = self.vm.get_vars(self.loader, host=host, include_hostvars=False) return hostvars def _get_group(self, gname): if self._new_api: group = self.inventory.groups.get(gname) else: group = self.inventory.get_group(gname) return group def _remove_internal(self, dump): for internal in INTERNAL_VARS: if internal in dump: del dump[internal] def _remove_empty(self, dump): # remove empty keys for x in ('hosts', 'vars', 'children'): if x in dump and not dump[x]: del dump[x] def _show_vars(self, dump, depth): result = [] self._remove_internal(dump) if self.options.show_vars: for (name, val) in sorted(dump.items()): result.append( self._graph_name('{%s = %s}' % (name, val), depth)) return result def _graph_name(self, name, depth=0): if depth: name = " |" * (depth) + "--%s" % name return name def _graph_group(self, group, depth=0): result = [self._graph_name('@%s:' % group.name, depth)] depth = depth + 1 for kid in sorted(group.child_groups, key=attrgetter('name')): result.extend(self._graph_group(kid, depth)) if group.name != 'all': for host in sorted(group.hosts, key=attrgetter('name')): result.append(self._graph_name(host.name, depth)) result.extend(self._show_vars(host.get_vars(), depth + 1)) result.extend(self._show_vars(self._get_group_variables(group), depth)) return result def inventory_graph(self): start_at = self._get_group(self.options.pattern) if start_at: return '\n'.join(self._graph_group(start_at)) else: raise AnsibleOptionsError( "Pattern must be valid group name when using --graph") def json_inventory(self, top): def format_group(group): results = {} results[group.name] = {} if group.name != 'all': results[group.name]['hosts'] = [ h.name for h in sorted(group.hosts, key=attrgetter('name')) ] results[group.name]['children'] = [] for subgroup in sorted(group.child_groups, key=attrgetter('name')): results[group.name]['children'].append(subgroup.name) results.update(format_group(subgroup)) if self.options.export: results[group.name]['vars'] = self._get_group_variables(group) self._remove_empty(results[group.name]) return results results = format_group(top) # populate meta results['_meta'] = {'hostvars': {}} hosts = self.inventory.get_hosts() for host in hosts: hvars = self._get_host_variables(host) if hvars: self._remove_internal(hvars) results['_meta']['hostvars'][host.name] = hvars return results def yaml_inventory(self, top): seen = [] def format_group(group): results = {} # initialize group + vars results[group.name] = {} # subgroups results[group.name]['children'] = {} for subgroup in sorted(group.child_groups, key=attrgetter('name')): if subgroup.name != 'all': results[group.name]['children'].update( format_group(subgroup)) # hosts for group results[group.name]['hosts'] = {} if group.name != 'all': for h in sorted(group.hosts, key=attrgetter('name')): myvars = {} if h.name not in seen: # avoid defining host vars more than once seen.append(h.name) myvars = self._get_host_variables(host=h) self._remove_internal(myvars) results[group.name]['hosts'][h.name] = myvars if self.options.export: gvars = self._get_group_variables(group) if gvars: results[group.name]['vars'] = gvars self._remove_empty(results[group.name]) return results return format_group(top)
class InventoryCLI(CLI): ''' used to display or dump the configured inventory as Ansible sees it ''' ARGUMENTS = { 'host': 'The name of a host to match in the inventory, relevant when using --list', 'group': 'The name of a group in the inventory, relevant when using --graph', } def __init__(self, args): super(InventoryCLI, self).__init__(args) self.args = args self.vm = None self.loader = None self.inventory = None self._new_api = True def parse(self): self.parser = CLI.base_parser( usage='usage: %prog [options] [host|group]', epilog= 'Show Ansible inventory information, by default it uses the inventory script JSON format', inventory_opts=True, vault_opts=True) self.parser.add_option( "--optimize", action="store_true", default=False, dest='optimize', help='Output variables on the group or host where they are defined' ) # Actions action_group = optparse.OptionGroup( self.parser, "Actions", "One of following must be used on invocation, ONLY ONE!") action_group.add_option( "--list", action="store_true", default=False, dest='list', help='Output all hosts info, works as inventory script') action_group.add_option( "--host", action="store", default=None, dest='host', help='Output specific host info, works as inventory script') action_group.add_option( "--graph", action="store_true", default=False, dest='graph', help= 'create inventory graph, if supplying pattern it must be a valid group name' ) self.parser.add_option_group(action_group) # Options self.parser.add_option( "-y", "--yaml", action="store_true", default=False, dest='yaml', help='Use YAML format instead of default JSON, ignored for --graph' ) self.parser.add_option( "--vars", action="store_true", default=False, dest='show_vars', help='Add vars to graph display, ignored unless used with --graph') try: super(InventoryCLI, self).parse() except Exception as e: if 'Need to implement!' not in e.args[0]: raise # --- Start of 2.3+ super(InventoryCLI, self).parse() --- self.options, self.args = self.parser.parse_args(self.args[1:]) # --- End of 2.3+ super(InventoryCLI, self).parse() --- display.verbosity = self.options.verbosity self.validate_conflicts(vault_opts=True) # there can be only one! and, at least, one! used = 0 for opt in (self.options.list, self.options.host, self.options.graph): if opt: used += 1 if used == 0: raise AnsibleOptionsError( "No action selected, at least one of --host, --graph or --list needs to be specified." ) elif used > 1: raise AnsibleOptionsError( "Conflicting options used, only one of --host, --graph or --list can be used at the same time." ) # set host pattern to default if not supplied if len(self.args) > 0: self.options.pattern = self.args[0] else: self.options.pattern = 'all' 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) def dump(self, stuff): if self.options.yaml: import yaml from ansible.parsing.yaml.dumper import AnsibleDumper results = yaml.dump(stuff, Dumper=AnsibleDumper, default_flow_style=False) else: import json results = json.dumps(stuff, sort_keys=True, indent=4) return results def _get_host_variables(self, host): if self._new_api: hostvars = self.vm.get_vars(host=host) else: hostvars = self.vm.get_vars(self.loader, host=host) return hostvars def _get_group(self, gname): if self._new_api: group = self.inventory.groups.get(gname) else: group = self.inventory.get_group(gname) return group def _remove_internal(self, dump): for internal in INTERNAL_VARS: if internal in dump: del dump[internal] def _remove_empty(self, dump): # remove empty keys for x in ('hosts', 'vars', 'children'): if x in dump and not dump[x]: del dump[x] def _show_vars(self, dump, depth): result = [] self._remove_internal(dump) if self.options.show_vars: for (name, val) in sorted(dump.items()): result.append( self._graph_name('{%s = %s}' % (name, val), depth + 1)) return result def _graph_name(self, name, depth=0): if depth: name = " |" * (depth) + "--%s" % name return name def _graph_group(self, group, depth=0): result = [self._graph_name('@%s:' % group.name, depth)] depth = depth + 1 for kid in sorted(group.child_groups, key=attrgetter('name')): result.extend(self._graph_group(kid, depth)) if group.name != 'all': for host in sorted(group.hosts, key=attrgetter('name')): result.append(self._graph_name(host.name, depth)) result.extend(self._show_vars(host.get_vars(), depth)) result.extend(self._show_vars(group.get_vars(), depth)) return result def inventory_graph(self): start_at = self._get_group(self.options.pattern) if start_at: return '\n'.join(self._graph_group(start_at)) else: raise AnsibleOptionsError( "Pattern must be valid group name when using --graph") def json_inventory(self, top): def format_group(group): results = {} results[group.name] = {} if group.name != 'all': results[group.name]['hosts'] = [ h.name for h in sorted(group.hosts, key=attrgetter('name')) ] results[group.name]['vars'] = group.get_vars() results[group.name]['children'] = [] for subgroup in sorted(group.child_groups, key=attrgetter('name')): results[group.name]['children'].append(subgroup.name) results.update(format_group(subgroup)) self._remove_empty(results[group.name]) return results results = format_group(top) # populate meta results['_meta'] = {'hostvars': {}} hosts = self.inventory.get_hosts() for host in hosts: results['_meta']['hostvars'][host.name] = self._get_host_variables( host=host) self._remove_internal(results['_meta']['hostvars'][host.name]) return results def yaml_inventory(self, top): seen = [] def format_group(group): results = {} # initialize group + vars results[group.name] = {} results[group.name]['vars'] = group.get_vars() # subgroups results[group.name]['children'] = {} for subgroup in sorted(group.child_groups, key=attrgetter('name')): if subgroup.name != 'all': results[group.name]['children'].update( format_group(subgroup)) # hosts for group results[group.name]['hosts'] = {} if group.name != 'all': for h in sorted(group.hosts, key=attrgetter('name')): myvars = {} if h.name not in seen: # avoid defining host vars more than once seen.append(h.name) myvars = self._get_host_variables(host=h) self._remove_internal(myvars) results[group.name]['hosts'][h.name] = myvars self._remove_empty(results[group.name]) return results return format_group(top)
class Runner(object): def __init__(self, hostnames, playbook, private_key_file, run_data, become_pass, verbosity=0): self.run_data = run_data self.options = Options() self.options.private_key_file = private_key_file self.options.verbosity = verbosity self.options.connection = 'ssh' # Need a connection type "smart" or "ssh" self.options.become = True self.options.become_method = 'sudo' self.options.become_user = '******' # Set global verbosity self.display = Display() self.display.verbosity = self.options.verbosity # Executor appears to have it's own # verbosity object/setting as well playbook_executor.verbosity = self.options.verbosity # Become Pass Needed if not logging in as user root passwords = {'become_pass': become_pass} # Gets data from YAML/JSON files self.loader = DataLoader() self.loader.set_vault_password(os.environ['VAULT_PASS']) # All the variables from all the various places self.variable_manager = VariableManager() self.variable_manager.extra_vars = self.run_data # Parse hosts, I haven't found a good way to # pass hosts in without using a parsed template :( # (Maybe you know how?) self.hosts = NamedTemporaryFile(delete=False) self.hosts.write("""[run_hosts] %s """ % hostnames) self.hosts.close() # This was my attempt to pass in hosts directly. # # Also Note: In py2.7, "isinstance(foo, str)" is valid for # latin chars only. Luckily, hostnames are # ascii-only, which overlaps latin charset ## if isinstance(hostnames, str): ## hostnames = {"customers": {"hosts": [hostnames]}} # Set inventory, using most of above objects self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=self.hosts.name) self.variable_manager.set_inventory(self.inventory) # Playbook to run. Assumes it is # local to this python file pb_dir = os.path.dirname(__file__) playbook = "%s/%s" % (pb_dir, playbook) # Setup playbook executor, but don't run until run() called self.pbex = playbook_executor.PlaybookExecutor( playbooks=[playbook], inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=passwords) def run(self): # Results of PlaybookExecutor self.pbex.run() stats = self.pbex._tqm._stats # Test if success for record_logs run_success = True hosts = sorted(stats.processed.keys()) for h in hosts: t = stats.summarize(h) if t['unreachable'] > 0 or t['failures'] > 0: run_success = False # Dirty hack to send callback to save logs with data we want # Note that function "record_logs" is one I created and put into # the playbook callback file self.pbex._tqm.send_callback('record_logs', user_id=self.run_data['user_id'], success=run_success) # Remove created temporary files os.remove(self.hosts.name) return stats
def run_command(self, name, ctx=None): cmd = self.able_file.get_command(name) from able.ablefile.command import AbleCommand if not isinstance(cmd, AbleCommand): return 0 cmd.ablefile = self.able_file params, global_params = utils.filter_params(ctx.params) self.options = self.get_global_option_values(cmd, global_params) if 'verbosity' in self.options: display.verbosity = self.options.verbosity # Note: slightly wrong, this is written so that implicit localhost # Manage passwords sshpass = None becomepass = None vault_pass = None passwords = {} # 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() (conn_pass, become_pass) = self.ask_passwords() passwords['conn_pass'] = conn_pass if conn_pass is not None else self.options.conn_pass passwords['become_pass'] = become_pass if become_pass is not None else self.options.become_pass loader = DataLoader() if self.options.vault_password_file: # read vault_pass from a file vault_pass = CLI.read_vault_password_file(self.options.vault_password_file, loader=loader) loader.set_vault_password(vault_pass) elif self.options.ask_vault_pass: vault_pass = self.ask_vault_passwords()[0] loader.set_vault_password(vault_pass) variable_manager = cmd.get_variable_manager() variable_manager.extra_vars = cmd.get_extra_vars(params, self.options) loader = cmd.get_loader() # create inventory and pass to var manager inventory = AbleInventory( loader=loader, variable_manager=variable_manager, host_list=cmd.get_inventory(), inventory_files=cmd.get_inventory_files() ) variable_manager.set_inventory(inventory) # Run any confirmation prompts cmd.do_confirm(variable_manager, loader) # create the playbook executor, which manages running the plays via a task queue manager ex = AbleCommandExecutor( command=cmd, inventory=inventory, variable_manager=variable_manager, loader=loader, options=self.options, passwords=passwords) results = ex.run() if isinstance(results, list): for p in results: display.display('\nCommand: %s' % p['command']) for idx, play in enumerate(p['plays']): msg = "\n command #%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' all_vars = cmd.get_variable_manager().get_vars(loader=cmd.get_loader(), play=self) play_context = PlayContext(play=self, options=self.options) for block in play.compile(): block = block.filter_tagged_tasks(play_context, all_vars) if not block.has_tasks(): continue for task in block.block: 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) 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
class Runner(object): def __init__(self, playbook, inventory, run_data=None, verbosity=0, tags=None, skip_tags=None): self.run_data = run_data or {} self.options = Options() self.options.verbosity = verbosity self.options.connection = 'local' # Need a connection type "smart" or "ssh" self.options.become = True self.options.become_method = 'sudo' self.options.become_user = '******' self.options.tags = tags or [] self.options.skip_tags = skip_tags or [] # Set global verbosity self.display = Display() self.display.verbosity = self.options.verbosity # Executor appears to have it's own # verbosity object/setting as well playbook_executor.verbosity = self.options.verbosity # Become Pass Needed if not logging in as user root passwords = {} # Gets data from YAML/JSON files self.loader = DataLoader() self.loader.set_vault_password(os.environ.get('VAULT_PASS', '')) # All the variables from all the various places self.variable_manager = VariableManager() self.variable_manager.extra_vars = self.run_data self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=inventory) self.variable_manager.set_inventory(self.inventory) # Setup playbook executor, but don't run until run() called self.pbex = playbook_executor.PlaybookExecutor( playbooks=[playbook], inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=passwords) def run(self, verbose=False): if not verbose: # Results of PlaybookExecutor cb = DisplayErrorCallback() self.pbex._tqm._stdout_callback = cb try: res = self.pbex.run() except AnsibleParserError as err: print(err) return None stats = self.pbex._tqm._stats # Test if success for record_logs run_success = True hosts = sorted(stats.processed.keys()) for h in hosts: t = stats.summarize(h) if t['unreachable'] > 0 or t['failures'] > 0: run_success = False return stats
def execute(self, hosts, environment, module="shell", command="ls"): Options = namedtuple('Options', [ 'connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check' ]) # initialize needed objects variable_manager = VariableManager() loader = DataLoader() options = Options(connection='ssh', module_path=None, forks=10, become=None, become_method=None, become_user=None, check=False) passwords = {} if self.vault_password_file: passwords = dict( vault_pass=open(os.path.expanduser(self.vault_password_file), 'r').read().strip()) loader.set_vault_password(passwords['vault_pass']) # Instantiate our ResultCallback for handling results as they come in results_callback = ResultCallback() # create inventory and pass to var manager inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list=environment) variable_manager.set_inventory(inventory) # create play with tasks play_source = dict( name=self.play_name, hosts=hosts, gather_facts='no', tasks=[ dict(action=dict(module=module, args=command), register='shell_out'), dict(action=dict(module='debug', args=dict(msg='{{shell_out.stdout}}'))) ]) play = Play().load(play_source, variable_manager=variable_manager, loader=loader) # actually run it tqm = None try: logger.info( "Executing: command [%s] in play %s on hosts %s in environment %s", command, self.play_name, hosts, environment) tqm = TaskQueueManager( inventory=inventory, variable_manager=variable_manager, loader=loader, options=options, passwords=passwords, stdout_callback= results_callback, # Use our custom callback instead of the ``default`` callback plugin ) result = tqm.run(play) finally: if tqm is not None: tqm.cleanup()
class Runner(object): def __init__( self, playbooks, tags, # must have extra_vars, hostnames='127.0.0.1', connection='local', # smart|ssh|local private_key_file='', become_pass='', vault_pass='', verbosity=0, debug=False): self.debug = debug self.options = Options() self.options.tags = tags, self.options.private_key_file = private_key_file self.options.verbosity = verbosity self.options.connection = connection self.options.become = True self.options.become_method = 'sudo' self.options.become_user = '******' self.options.extra_vars = extra_vars # Set global verbosity self.display = Display() self.display.verbosity = self.options.verbosity # Executor appears to have it's own # verbosity object/setting as well playbook_executor.verbosity = self.options.verbosity # Become Pass Needed if not logging in as user root passwords = {'become_pass': become_pass} # Gets data from YAML/JSON files self.loader = DataLoader() self.loader.set_vault_password(vault_pass) # All the variables from all the various places self.variable_manager = VariableManager() self.variable_manager.extra_vars = extra_vars # Parse hosts, I haven't found a good way to # pass hosts in without using a parsed template :( # (Maybe you know how?) self.hosts = NamedTemporaryFile(delete=False, mode='wt') self.hosts.write("""[run_hosts]\n%s""" % hostnames) self.hosts.close() # This was my attempt to pass in hosts directly. # # Also Note: In py2.7, "isinstance(foo, str)" is valid for # latin chars only. Luckily, hostnames are # ascii-only, which overlaps latin charset # if isinstance(hostnames, str): # hostnames = {"customers": {"hosts": [hostnames]}} # Set inventory, using most of above objects self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=self.hosts.name) self.variable_manager.set_inventory(self.inventory) # Playbook to run. Assumes it is # local and relative to this python file # in "../../../playbooks" directory. dirname = os.path.dirname(__file__) or '.' pb_rel_dir = '../../../playbooks' playbook_path = os.path.join(dirname, pb_rel_dir) self.options.module_path = os.path.join(playbook_path, 'library') # os.environ['ANSIBLE_CONFIG'] = os.path.abspath(os.path.dirname(__file__)) pbs = [os.path.join(playbook_path, pb) for pb in playbooks] # Setup playbook executor, but don't run until run() called self.pbex = playbook_executor.PlaybookExecutor( playbooks=pbs, inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=passwords) # TODO: so here we construct a CLI line. # For whatever reason, api is not taking account for `tags`!! self.callme = [ 'ansible-playbook', '-i', self.hosts.name, ','.join(pbs), ] if tags: self.callme += ['--tags', ','.join(tags)] if extra_vars: extra = ["%s=%s" % (k, v) for k, v in extra_vars.items()] self.callme += ['--extra-vars', '"%s"' % (' '.join(extra))] if self.options.module_path: self.callme += ['--module-path', self.options.module_path] def run(self): # Results of PlaybookExecutor if self.debug: print "ansbile cmd: ", ' '.join(self.callme) # self.pbex.run() # TODO: this is truly strange! I tried all subprocess calls, # and this is the ONLY way that works! All others didn't, even though # the printed cmd line is exactly the same, and can be executed # if I copy & paste in a terminal. So strange! return_code = subprocess.call(' '.join(self.callme), shell=True) return True if return_code == 0 else False
private_key_file=None, ssh_common_args=None, ssh_extra_args=None, sftp_extra_args=None, scp_extra_args=None, become=None, become_method='sudo', become_user='******', verbosity=None, check=False) loader = DataLoader() if os.path.exists('/tmp/vault_pass.txt'): with open('/tmp/vault_pass.txt', 'r') as vaultfile: data=vaultfile.read().replace('\n', '') loader.set_vault_password(data) variable_manager = VariableManager() variable_manager.extra_vars.update(args.extra_vars) # we apply an exhaustive list of groups to the host that is created in the # inventory, simply to ensure that we resolve the correct hosts for each # play. This has the unfortunate side effect that if someone has a # group_var that shadows the name of a play, it could be applied unexpectedly. # TODO: look up plays by name and fetch the host_groups explicitly to be added. inventory_host_groups = args.groups + args.hosts + args.plays inventory = build_inventory(loader, variable_manager, inventory_host_groups, playbook_basedir) variable_manager.set_inventory(inventory) plays = build_plays(loader, variable_manager, args.playbook, plays=args.plays, hosts=args.hosts)
class Runner(object): def __init__( self, playbook, display, hosts='hosts', options={}, passwords={}, vault_pass=None): # Set options self.options = Options() for k, v in options.iteritems(): setattr(self.options, k, v) # Set global verbosity self.display = display self.display.verbosity = self.options.verbosity # Executor has its own verbosity setting playbook_executor.verbosity = self.options.verbosity # Gets data from YAML/JSON files self.loader = DataLoader() # Set vault password if vault_pass is not None: self.loader.set_vault_password(vault_pass) elif 'VAULT_PASS' in os.environ: self.loader.set_vault_password(os.environ['VAULT_PASS']) # All the variables from all the various places self.variable_manager = VariableManager() if self.options.python_interpreter is not None: self.variable_manager.extra_vars = { 'ansible_python_interpreter': self.options.python_interpreter } # Set inventory, using most of above objects self.inventory = Inventory( loader=self.loader, variable_manager=self.variable_manager, host_list=hosts) if len(self.inventory.list_hosts()) == 0: # Empty inventory self.display.error("Provided hosts list is empty.") sys.exit(1) self.inventory.subset(self.options.subset) if len(self.inventory.list_hosts()) == 0: # Invalid limit self.display.error("Specified limit does not match any hosts.") sys.exit(1) self.variable_manager.set_inventory(self.inventory) # import pdb; pdb.set_trace() # Setup playbook executor, but don't run until run() called self.pbex = playbook_executor.PlaybookExecutor( playbooks=[playbook], inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=passwords) def run(self): # Run Playbook and get stats self.pbex.run() stats = self.pbex._tqm._stats # Test if success for record_logs run_success = True hosts = sorted(stats.processed.keys()) for h in hosts: t = stats.summarize(h) if t['unreachable'] > 0 or t['failures'] > 0: run_success = False # Dirty hack to send callback to save logs with data we want # Note that function "record_logs" is one I created and put into # the playbook callback file # import pdb; pdb.set_trace() self.pbex._tqm.send_callback('record_logs') # os.remove(self.hosts.name) return stats
class ConsoleCLI(CLI, cmd.Cmd): modules = [] def __init__(self, args): super(ConsoleCLI, self).__init__(args) self.intro = 'Welcome to the ansible console.\nType help or ? to list commands.\n' self.groups = [] self.hosts = [] self.pattern = None self.variable_manager = None self.loader = None self.passwords = dict() self.modules = None cmd.Cmd.__init__(self) def parse(self): self.parser = CLI.base_parser( usage='%prog <host-pattern> [options]', runas_opts=True, inventory_opts=True, connect_opts=True, check_opts=True, vault_opts=True, fork_opts=True, module_opts=True, ) # options unique to shell self.parser.add_option('--step', dest='step', action='store_true', help="one-step-at-a-time: confirm each task before running") self.parser.set_defaults(cwd='*') self.options, self.args = self.parser.parse_args(self.args[1:]) display.verbosity = self.options.verbosity self.validate_conflicts(runas_opts=True, vault_opts=True, fork_opts=True) return True def get_names(self): return dir(self) def cmdloop(self): try: cmd.Cmd.cmdloop(self) except KeyboardInterrupt: self.do_exit(self) def set_prompt(self): login_user = self.options.remote_user or getpass.getuser() self.selected = self.inventory.list_hosts(self.options.cwd) prompt = "%s@%s (%d)[f:%s]" % (login_user, self.options.cwd, len(self.selected), self.options.forks) if self.options.become and self.options.become_user in [None, 'root']: prompt += "# " color = C.COLOR_ERROR else: prompt += "$ " color = C.COLOR_HIGHLIGHT self.prompt = stringc(prompt, color) def list_modules(self): modules = set() if self.options.module_path is not None: for i in self.options.module_path.split(os.pathsep): module_loader.add_directory(i) module_paths = module_loader._get_paths() for path in module_paths: if path is not None: modules.update(self._find_modules_in_path(path)) return modules def _find_modules_in_path(self, path): if os.path.isdir(path): for module in os.listdir(path): if module.startswith('.'): continue elif os.path.isdir(module): self._find_modules_in_path(module) elif module.startswith('__'): continue elif any(module.endswith(x) for x in C.BLACKLIST_EXTS): continue elif module in C.IGNORE_FILES: continue elif module.startswith('_'): fullpath = '/'.join([path,module]) if os.path.islink(fullpath): # avoids aliases continue module = module.replace('_', '', 1) module = os.path.splitext(module)[0] # removes the extension yield module def default(self, arg, forceshell=False): """ actually runs modules """ if arg.startswith("#"): return False if not self.options.cwd: display.error("No host found") return False if arg.split()[0] in self.modules: module = arg.split()[0] module_args = ' '.join(arg.split()[1:]) else: module = 'shell' module_args = arg if forceshell is True: module = 'shell' module_args = arg self.options.module_name = module result = None try: check_raw = self.options.module_name in ('command', 'shell', 'script', 'raw') play_ds = dict( name = "Ansible Shell", hosts = self.options.cwd, gather_facts = 'no', tasks = [ dict(action=dict(module=module, args=parse_kv(module_args, check_raw=check_raw)))] ) play = Play().load(play_ds, variable_manager=self.variable_manager, loader=self.loader) except Exception as e: display.error(u"Unable to build command: %s" % to_text(e)) return False try: cb = 'minimal' # FIXME: make callbacks configurable # now create a task queue manager to execute the play self._tqm = None try: self._tqm = TaskQueueManager( inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=self.passwords, stdout_callback=cb, run_additional_callbacks=C.DEFAULT_LOAD_CALLBACK_PLUGINS, run_tree=False, ) result = self._tqm.run(play) finally: if self._tqm: self._tqm.cleanup() if self.loader: self.loader.cleanup_all_tmp_files() if result is None: display.error("No hosts found") return False except KeyboardInterrupt: display.error('User interrupted execution') return False except Exception as e: display.error(to_text(e)) # FIXME: add traceback in very very verbose mode return False def emptyline(self): return def do_shell(self, arg): """ You can run shell commands through the shell module. eg.: shell ps uax | grep java | wc -l shell killall python shell halt -n You can use the ! to force the shell module. eg.: !ps aux | grep java | wc -l """ self.default(arg, True) 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() do_serial = do_forks 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) def do_cd(self, arg): """ Change active host/group. You can use hosts patterns as well eg.: cd webservers cd webservers:dbservers cd webservers:!phoenix cd webservers:&staging cd webservers:dbservers:&staging:!phoenix """ if not arg: self.options.cwd = '*' elif arg == '..': try: self.options.cwd = self.inventory.groups_for_host(self.options.cwd)[1].name except Exception: self.options.cwd = '' elif arg in '/*': self.options.cwd = 'all' elif self.inventory.get_hosts(arg): self.options.cwd = arg else: display.display("no host matched") self.set_prompt() 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) 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`") 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`") 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() 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`") 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`") 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`") def do_exit(self, args): """Exits from the console""" sys.stdout.write('\n') return -1 do_EOF = do_exit def helpdefault(self, module_name): if module_name in self.modules: in_path = module_loader.find_plugin(module_name) if in_path: oc, a, _ = module_docs.get_docstring(in_path) if oc: display.display(oc['short_description']) display.display('Parameters:') for opt in oc['options'].keys(): display.display(' ' + stringc(opt, C.COLOR_HIGHLIGHT) + ' ' + oc['options'][opt]['description'][0]) else: display.error('No documentation found for %s.' % module_name) else: display.error('%s is not a valid command, use ? to list all valid commands.' % module_name) def complete_cd(self, text, line, begidx, endidx): mline = line.partition(' ')[2] offs = len(mline) - len(text) if self.options.cwd in ('all','*','\\'): completions = self.hosts + self.groups else: completions = [x.name for x in self.inventory.list_hosts(self.options.cwd)] return [to_native(s)[offs:] for s in completions if to_native(s).startswith(to_native(mline))] def completedefault(self, text, line, begidx, endidx): if line.split()[0] in self.modules: mline = line.split(' ')[-1] offs = len(mline) - len(text) completions = self.module_args(line.split()[0]) return [s[offs:] + '=' for s in completions if s.startswith(mline)] def module_args(self, module_name): in_path = module_loader.find_plugin(module_name) oc, a, _ = module_docs.get_docstring(in_path) return oc['options'].keys() def run(self): super(ConsoleCLI, self).run() sshpass = None becomepass = None vault_pass = None # hosts if len(self.args) != 1: self.pattern = 'all' else: self.pattern = self.args[0] self.options.cwd = self.pattern # dynamically add modules as commands self.modules = self.list_modules() for module in self.modules: setattr(self, 'do_' + module, lambda arg, module=module: self.default(module + ' ' + arg)) setattr(self, 'help_' + module, lambda module=module: self.helpdefault(module)) self.normalize_become_options() (sshpass, becomepass) = self.ask_passwords() self.passwords = { 'conn_pass': sshpass, 'become_pass': becomepass } self.loader = DataLoader() if self.options.vault_password_file: # read vault_pass from a file vault_pass = CLI.read_vault_password_file(self.options.vault_password_file, loader=self.loader) self.loader.set_vault_password(vault_pass) elif self.options.ask_vault_pass: vault_pass = self.ask_vault_passwords()[0] self.loader.set_vault_password(vault_pass) self.variable_manager = VariableManager() self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=self.options.inventory) self.variable_manager.set_inventory(self.inventory) no_hosts = False if len(self.inventory.list_hosts()) == 0: # Empty inventory no_hosts = True display.warning("provided hosts list is empty, only localhost is available") self.inventory.subset(self.options.subset) hosts = self.inventory.list_hosts(self.pattern) if len(hosts) == 0 and not no_hosts: raise AnsibleError("Specified hosts and/or --limit does not match any hosts") self.groups = self.inventory.list_groups() self.hosts = [x.name for x in hosts] # This hack is to work around readline issues on a mac: # http://stackoverflow.com/a/7116997/541202 if 'libedit' in readline.__doc__: readline.parse_and_bind("bind ^I rl_complete") else: readline.parse_and_bind("tab: complete") histfile = os.path.join(os.path.expanduser("~"), ".ansible-console_history") try: readline.read_history_file(histfile) except IOError: pass atexit.register(readline.write_history_file, histfile) self.set_prompt() self.cmdloop()
def parse_yaml_from_file(filepath: str) -> AnsibleBaseYAMLObject: dl = DataLoader() if hasattr(dl, 'set_vault_password'): dl.set_vault_password(DEFAULT_VAULT_PASSWORD) return dl.load_from_file(filepath)
class Runner(object): def __init__(self, groups, playbook, private_key_file, display, become_pass={}, extraVars={}): self.extraVars = extraVars self.options = Options() self.options.private_key_file = private_key_file self.options.verbosity = display.verbosity self.options.connection = 'ssh' # Need a connection type 'smart' or 'ssh' self.options.become = True self.options.become_method = 'sudo' self.options.become_user = '******' # Executor appears to have it's own # verbosity object/setting as well playbook_executor.verbosity = self.options.verbosity # Become Pass Needed if not logging in as user root passwords = {'become_pass':become_pass} # Gets data from YAML/JSON files self.loader = DataLoader() self.loader.set_vault_password(os.environ.get('VAULT_PASS', 'xiaoy_pandan')) # All the variables from all the various places self.variable_manager = VariableManager() self.variable_manager.extra_vars = self.extraVars # Set inventory, using most of above objects self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=[]) self.variable_manager.set_inventory(self.inventory) # Set groups info for info in groups: group = Group(info['name']) self.inventory.add_group(group) hostInfos = info.get('hosts') for hostInfo in hostInfos: hostname = hostInfo.get('hostname') hostip = hostInfo.get('ip', hostname) hostport = hostInfo.get('port') username = hostInfo.get('username') password = hostInfo.get('password') ssh_key = hostInfo.get('ssh_key') host = Host(name=hostname, port=hostport) host.set_variable('ansible_ssh_host', hostip) host.set_variable('ansible_ssh_port', hostport) host.set_variable('ansible_ssh_user', username) host.set_variable('ansible_ssh_pass', password) host.set_variable('ansible_ssh_private_key_file', ssh_key) for key, value in hostInfo.iteritems(): if key not in ['hostname', 'port', 'username', 'password', 'ssh_key']: host.set_variable(key, value) group.add_host(host) varInfos = info.get('vars') if varInfos: for key, value in varInfos.iteritems(): group.set_variable(key, value) # Playbook to run. Assumes it is # local to this python file pb_dir = os.path.dirname(__file__) playbook = '%s/%s' % (pb_dir, playbook) # Setup playbook executor, but don't run until run() called self.pbex = playbook_executor.PlaybookExecutor( playbooks=[playbook], inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=passwords) self.display = display self.callback = CallbackModule(self.display) self.pbex._tqm._stdout_callback = self.callback def run(self): # Results of PlaybookExecutor self.pbex.run() # calc result result = True hostResults = {} stats = self.pbex._tqm._stats hosts = sorted(stats.processed.keys()) for h in hosts: t = stats.summarize(h) if t['unreachable'] > 0 or t['failures'] > 0: hostResult = result = False else: hostResult = True hostResults[h] = hostResult if result: self.display.display('success') else: self.display.display('fail', stderr=True) self.display.display('') return result, hostResults
class Runner(object): def __init__(self, playbook, run_data, private_key_file=None, verbosity=0, host_file=None): self.playbook = playbook self.run_data = run_data self.options = Options() self.options.output_file = playbook + '.result' self.options.private_key_file = private_key_file self.options.verbosity = verbosity self.options.connection = 'ssh' # Need a connection type "smart" or "ssh" #self.options.become = True self.options.become_method = 'sudo' self.options.become_user = '******' # Set global verbosity self.display = Display() self.display.verbosity = self.options.verbosity # Executor appears to have it's own # verbosity object/setting as well playbook_executor.verbosity = self.options.verbosity # Become Pass Needed if not logging in as user root #passwords = {'become_pass': become_pass} # Gets data from YAML/JSON files self.loader = DataLoader() try: self.loader.set_vault_password(os.environ['VAULT_PASS']) except KeyError: pass # All the variables from all the various places self.variable_manager = VariableManager() self.variable_manager.extra_vars = {} # self.run_data # Set inventory, using most of above objects if (host_file): self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list = host_file) else: self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager) self.variable_manager.set_inventory(self.inventory) # Setup playbook executor, but don't run until run() called self.pbex = playbook_executor.PlaybookExecutor( playbooks=[playbook], inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords={}) def run(self): os.environ['REQUESTS_CA_BUNDLE'] = '/usr/local/share/ca-certificates/local_certs.crt' callback = ResultCallback() self.pbex._tqm._stdout_callback = callback self.pbex.run() stats = self.pbex._tqm._stats # Test if success for record_logs run_success = True hosts = sorted(stats.processed.keys()) for h in hosts: t = stats.summarize(h) if t['unreachable'] > 0 or t['failures'] > 0: run_success = False #os.remove(self.hosts.name) return stats,callback.results
class Runner(object): def __init__(self, playbook, options={}, passwords={}, vault_pass=None): display = Display() ansible_root = '/opt/emc' pb_dir = "{0}/playbooks".format(ansible_root) playbook = "{0}/{1}".format(pb_dir, playbook) hosts = "{0}/hosts.ini".format(ansible_root) # Set options self.options = Options() for k, v in options.iteritems(): setattr(self.options, k, v) self.options.verbosity = 0 # Set global verbosity self.display = display self.display.verbosity = self.options.verbosity # Executor has its own verbosity setting playbook_executor.verbosity = self.options.verbosity # Gets data from YAML/JSON files self.loader = DataLoader() # Set vault password if vault_pass is not None: self.loader.set_vault_password(vault_pass) elif 'VAULT_PASS' in os.environ: self.loader.set_vault_password(os.environ['VAULT_PASS']) # All the variables from all the various places self.variable_manager = VariableManager() if self.options.python_interpreter is not None: self.variable_manager.extra_vars = { 'ansible_python_interpreter': self.options.python_interpreter } # Set inventory, using most of above objects self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=hosts) if len(self.inventory.list_hosts()) == 0: # Empty inventory self.display.error("Provided hosts list is empty.") sys.exit(1) self.inventory.subset(self.options.subset) if len(self.inventory.list_hosts()) == 0: # Invalid limit self.display.error("Specified limit does not match any hosts.") sys.exit(1) self.variable_manager.set_inventory(self.inventory) # Setup playbook executor, but don't run until run() called self.pbex = playbook_executor.PlaybookExecutor( playbooks=[playbook], inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=passwords) def run(self): # Run Playbook and get stats self.pbex.run() stats = self.pbex._tqm._stats run_success = True hosts = sorted(stats.processed.keys()) for h in hosts: t = stats.summarize(h) if t['unreachable'] > 0 or t['failures'] > 0: run_success = False # Do the callback # self.pbex._tqm.send_callback( # 'record_logs', # success=run_success # ) return run_success
class Runner(object): def __init__( self, playbook, options={}, passwords={}, vault_pass=None): display = Display() ansible_root = '/opt/emc' pb_dir = "{0}/playbooks".format(ansible_root) playbook = "{0}/{1}".format(pb_dir, playbook) hosts = "{0}/hosts.ini".format(ansible_root) # Set options self.options = Options() for k, v in options.iteritems(): setattr(self.options, k, v) self.options.verbosity = 0 # Set global verbosity self.display = display self.display.verbosity = self.options.verbosity # Executor has its own verbosity setting playbook_executor.verbosity = self.options.verbosity # Gets data from YAML/JSON files self.loader = DataLoader() # Set vault password if vault_pass is not None: self.loader.set_vault_password(vault_pass) elif 'VAULT_PASS' in os.environ: self.loader.set_vault_password(os.environ['VAULT_PASS']) # All the variables from all the various places self.variable_manager = VariableManager() if self.options.python_interpreter is not None: self.variable_manager.extra_vars = { 'ansible_python_interpreter': self.options.python_interpreter } # Set inventory, using most of above objects self.inventory = Inventory( loader=self.loader, variable_manager=self.variable_manager, host_list=hosts) if len(self.inventory.list_hosts()) == 0: # Empty inventory self.display.error("Provided hosts list is empty.") sys.exit(1) self.inventory.subset(self.options.subset) if len(self.inventory.list_hosts()) == 0: # Invalid limit self.display.error("Specified limit does not match any hosts.") sys.exit(1) self.variable_manager.set_inventory(self.inventory) # Setup playbook executor, but don't run until run() called self.pbex = playbook_executor.PlaybookExecutor( playbooks=[playbook], inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=passwords) def run(self): # Run Playbook and get stats self.pbex.run() stats = self.pbex._tqm._stats run_success = True hosts = sorted(stats.processed.keys()) for h in hosts: t = stats.summarize(h) if t['unreachable'] > 0 or t['failures'] > 0: run_success = False # Do the callback # self.pbex._tqm.send_callback( # 'record_logs', # success=run_success # ) return run_success
def run(self): # Note: slightly wrong, this is written so that implicit localhost # Manage passwords sshpass = None becomepass = None b_vault_pass = None passwords = {} # initial error check, to make sure all specified playbooks are accessible # before we start running anything through the playbook executor for playbook in self.playbooks: 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 = DataLoader() if self.options.vault_password_file: # read vault_pass from a file b_vault_pass = CLI.read_vault_password_file( self.options.vault_password_file, loader=loader) loader.set_vault_password(b_vault_pass) elif self.options.ask_vault_pass: b_vault_pass = self.ask_vault_passwords() loader.set_vault_password(b_vault_pass) elif 'VAULT_PASS' in os.environ: loader.set_vault_password(os.environ['VAULT_PASS']) # create the variable manager, which will be shared throughout # the code, ensuring a consistent view of global variables variable_manager = VariableManager() # Subspace injection option_extra_vars = load_extra_vars(loader=loader, options=self.options) option_extra_vars.update(self.extra_vars) variable_manager.extra_vars = option_extra_vars # End Subspace injection variable_manager.options_vars = load_options_vars(self.options) # create the inventory, and filter it based on the subset specified (if any) inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list=self.options.inventory) variable_manager.set_inventory(inventory) # (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) hosts = inventory.get_hosts() if self.options.subset and not hosts: raise NoValidHosts( "The limit <%s> is not included in the inventory: %s" % (self.options.subset, inventory.host_list)) # create the playbook executor, which manages running the plays via a task queue manager # Subspace injection pbex = PlaybookExecutor(playbooks=self.playbooks, inventory=inventory, variable_manager=variable_manager, loader=loader, options=self.options, passwords=passwords) playbook_map = self._get_playbook_map() pbex._tqm._stats = SubspaceAggregateStats(playbook_map) pbex._tqm.load_callbacks() pbex._tqm.send_callback( 'start_logging', logger=self.options.logger, username=self.extra_vars.get('ATMOUSERNAME', "No-User"), ) for host in inventory._subset: variables = inventory.get_vars(host) self.options.logger.info("Vars found for hostname %s: %s" % (host, variables)) # End Subspace injection results = pbex.run() # Subspace injection stats = pbex._tqm._stats self.stats = stats # Nonpersistent fact cache stores 'register' variables. We would like # to get access to stdout/stderr for specific commands and relay # some of that information back to the end user. self.results = dict(pbex._variable_manager._nonpersistent_fact_cache) # End Subspace injection 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(loader=loader, 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
class ConsoleCLI(CLI, cmd.Cmd): modules = [] def __init__(self, args): super(ConsoleCLI, self).__init__(args) self.intro = 'Welcome to the ansible console.\nType help or ? to list commands.\n' self.groups = [] self.hosts = [] self.pattern = None self.variable_manager = None self.loader = None self.passwords = dict() self.modules = None cmd.Cmd.__init__(self) def parse(self): self.parser = CLI.base_parser( usage='%prog <host-pattern> [options]', runas_opts=True, inventory_opts=True, connect_opts=True, check_opts=True, vault_opts=True, fork_opts=True, module_opts=True, ) # options unique to shell self.parser.add_option( '--step', dest='step', action='store_true', help="one-step-at-a-time: confirm each task before running") self.parser.set_defaults(cwd='*') super(AdHocCLI, self).parse() display.verbosity = self.options.verbosity self.validate_conflicts(runas_opts=True, vault_opts=True, fork_opts=True) def get_names(self): return dir(self) def cmdloop(self): try: cmd.Cmd.cmdloop(self) except KeyboardInterrupt: self.do_exit(self) def set_prompt(self): login_user = self.options.remote_user or getpass.getuser() self.selected = self.inventory.list_hosts(self.options.cwd) prompt = "%s@%s (%d)[f:%s]" % (login_user, self.options.cwd, len(self.selected), self.options.forks) if self.options.become and self.options.become_user in [None, 'root']: prompt += "# " color = C.COLOR_ERROR else: prompt += "$ " color = C.COLOR_HIGHLIGHT self.prompt = stringc(prompt, color) def list_modules(self): modules = set() if self.options.module_path is not None: for i in self.options.module_path.split(os.pathsep): module_loader.add_directory(i) module_paths = module_loader._get_paths() for path in module_paths: if path is not None: modules.update(self._find_modules_in_path(path)) return modules def _find_modules_in_path(self, path): if os.path.isdir(path): for module in os.listdir(path): if module.startswith('.'): continue elif os.path.isdir(module): self._find_modules_in_path(module) elif module.startswith('__'): continue elif any(module.endswith(x) for x in C.BLACKLIST_EXTS): continue elif module in C.IGNORE_FILES: continue elif module.startswith('_'): fullpath = '/'.join([path, module]) if os.path.islink(fullpath): # avoids aliases continue module = module.replace('_', '', 1) module = os.path.splitext(module)[0] # removes the extension yield module def default(self, arg, forceshell=False): """ actually runs modules """ if arg.startswith("#"): return False if not self.options.cwd: display.error("No host found") return False if arg.split()[0] in self.modules: module = arg.split()[0] module_args = ' '.join(arg.split()[1:]) else: module = 'shell' module_args = arg if forceshell is True: module = 'shell' module_args = arg self.options.module_name = module result = None try: check_raw = self.options.module_name in ('command', 'shell', 'script', 'raw') play_ds = dict( name="Ansible Shell", hosts=self.options.cwd, gather_facts='no', tasks=[ dict(action=dict(module=module, args=parse_kv(module_args, check_raw=check_raw))) ]) play = Play().load(play_ds, variable_manager=self.variable_manager, loader=self.loader) except Exception as e: display.error(u"Unable to build command: %s" % to_text(e)) return False try: cb = 'minimal' # FIXME: make callbacks configurable # now create a task queue manager to execute the play self._tqm = None try: self._tqm = TaskQueueManager( inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=self.passwords, stdout_callback=cb, run_additional_callbacks=C.DEFAULT_LOAD_CALLBACK_PLUGINS, run_tree=False, ) result = self._tqm.run(play) finally: if self._tqm: self._tqm.cleanup() if self.loader: self.loader.cleanup_all_tmp_files() if result is None: display.error("No hosts found") return False except KeyboardInterrupt: display.error('User interrupted execution') return False except Exception as e: display.error(to_text(e)) # FIXME: add traceback in very very verbose mode return False def emptyline(self): return def do_shell(self, arg): """ You can run shell commands through the shell module. eg.: shell ps uax | grep java | wc -l shell killall python shell halt -n You can use the ! to force the shell module. eg.: !ps aux | grep java | wc -l """ self.default(arg, True) 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() do_serial = do_forks 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) def do_cd(self, arg): """ Change active host/group. You can use hosts patterns as well eg.: cd webservers cd webservers:dbservers cd webservers:!phoenix cd webservers:&staging cd webservers:dbservers:&staging:!phoenix """ if not arg: self.options.cwd = '*' elif arg == '..': try: self.options.cwd = self.inventory.groups_for_host( self.options.cwd)[1].name except Exception: self.options.cwd = '' elif arg in '/*': self.options.cwd = 'all' elif self.inventory.get_hosts(arg): self.options.cwd = arg else: display.display("no host matched") self.set_prompt() 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) 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`") 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`") 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() 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`") 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`") 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`") def do_exit(self, args): """Exits from the console""" sys.stdout.write('\n') return -1 do_EOF = do_exit def helpdefault(self, module_name): if module_name in self.modules: in_path = module_loader.find_plugin(module_name) if in_path: oc, a, _ = module_docs.get_docstring(in_path) if oc: display.display(oc['short_description']) display.display('Parameters:') for opt in oc['options'].keys(): display.display(' ' + stringc(opt, C.COLOR_HIGHLIGHT) + ' ' + oc['options'][opt]['description'][0]) else: display.error('No documentation found for %s.' % module_name) else: display.error( '%s is not a valid command, use ? to list all valid commands.' % module_name) def complete_cd(self, text, line, begidx, endidx): mline = line.partition(' ')[2] offs = len(mline) - len(text) if self.options.cwd in ('all', '*', '\\'): completions = self.hosts + self.groups else: completions = [ x.name for x in self.inventory.list_hosts(self.options.cwd) ] return [ to_native(s)[offs:] for s in completions if to_native(s).startswith(to_native(mline)) ] def completedefault(self, text, line, begidx, endidx): if line.split()[0] in self.modules: mline = line.split(' ')[-1] offs = len(mline) - len(text) completions = self.module_args(line.split()[0]) return [s[offs:] + '=' for s in completions if s.startswith(mline)] def module_args(self, module_name): in_path = module_loader.find_plugin(module_name) oc, a, _ = module_docs.get_docstring(in_path) return oc['options'].keys() def run(self): super(ConsoleCLI, self).run() sshpass = None becomepass = None vault_pass = None # hosts if len(self.args) != 1: self.pattern = 'all' else: self.pattern = self.args[0] self.options.cwd = self.pattern # dynamically add modules as commands self.modules = self.list_modules() for module in self.modules: setattr( self, 'do_' + module, lambda arg, module=module: self.default(module + ' ' + arg)) setattr(self, 'help_' + module, lambda module=module: self.helpdefault(module)) self.normalize_become_options() (sshpass, becomepass) = self.ask_passwords() self.passwords = {'conn_pass': sshpass, 'become_pass': becomepass} self.loader = DataLoader() if self.options.vault_password_file: # read vault_pass from a file vault_pass = CLI.read_vault_password_file( self.options.vault_password_file, loader=self.loader) self.loader.set_vault_password(vault_pass) elif self.options.ask_vault_pass: vault_pass = self.ask_vault_passwords()[0] self.loader.set_vault_password(vault_pass) self.variable_manager = VariableManager() self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=self.options.inventory) self.variable_manager.set_inventory(self.inventory) no_hosts = False if len(self.inventory.list_hosts()) == 0: # Empty inventory no_hosts = True display.warning( "provided hosts list is empty, only localhost is available") self.inventory.subset(self.options.subset) hosts = self.inventory.list_hosts(self.pattern) if len(hosts) == 0 and not no_hosts: raise AnsibleError( "Specified hosts and/or --limit does not match any hosts") self.groups = self.inventory.list_groups() self.hosts = [x.name for x in hosts] # This hack is to work around readline issues on a mac: # http://stackoverflow.com/a/7116997/541202 if 'libedit' in readline.__doc__: readline.parse_and_bind("bind ^I rl_complete") else: readline.parse_and_bind("tab: complete") histfile = os.path.join(os.path.expanduser("~"), ".ansible-console_history") try: readline.read_history_file(histfile) except IOError: pass atexit.register(readline.write_history_file, histfile) self.set_prompt() self.cmdloop()
class Runner(object): def __init__(self, hosts=None, playbook=None, remote_user='******', private_key_file='~/.ssh/id_rsa', become_pass='', verbosity=0, extra_vars={}): """ Arguments: hosts:- The hosts. See the docstring of InventoryWrapper.__init__ for more details on the acceptable formats. playbook:- is the path to the playbook file become_pass:- seems like the password for priviledge escalation """ if not hosts or not playbook: raise ValueError("hosts and playbook arguments must be defined") self.run_data = extra_vars self.options = Options() self.options.private_key_file = os.path.expanduser(private_key_file) self.options.verbosity = verbosity self.options.connection = 'ssh' # Need a connection type "smart" or "ssh" self.options.become = True self.options.become_method = 'sudo' self.options.become_user = '******' self.options.remote_user = remote_user # Set global verbosity self.display = Display() self.display.verbosity = self.options.verbosity # Executor appears to have it's own # verbosity object/setting as well playbook_executor.verbosity = self.options.verbosity # Become Pass Needed if not logging in as user root passwords = {'become_pass': become_pass} # Gets data from YAML/JSON files self.loader = DataLoader() self.loader.set_vault_password(os.environ['VAULT_PASS']) # All the variables from all the various places self.variable_manager = VariableManager() self.variable_manager.extra_vars = self.run_data # Parse hosts, I haven't found a good way to # pass hosts in without using a parsed template :( # (Maybe you know how?) self.hosts = NamedTemporaryFile(delete=False, dir=os.getcwd()) self.inventory_wrapper = InventoryWrapper(hosts) self.hosts.write(str(self.inventory_wrapper)) self.hosts.close() # Set inventory, using most of above objects self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=self.hosts.name) self.variable_manager.set_inventory(self.inventory) # Setup playbook executor, but don't run until run() called self.pbex = playbook_executor.PlaybookExecutor( playbooks=[playbook], inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=passwords) def run(self): # Results of PlaybookExecutor self.pbex.run() stats = self.pbex._tqm._stats # Test if success for record_logs run_success = True hosts = sorted(stats.processed.keys()) for h in hosts: t = stats.summarize(h) if t['unreachable'] > 0 or t['failures'] > 0: run_success = False # Dirty hack to send callback to save logs with data we want # Note that function "record_logs" is one I created and put into # the playbook callback file #FIXME: get rid of the following, since this is not being used self.pbex._tqm.send_callback( 'record_logs', user_id=None, #self.run_data['user_id'], success=run_success ) # Remove created temporary files os.remove(self.hosts.name) return stats
class SimpleProvider(object): __metaclass__ = ABCMeta def __init__(self, name, general_type=None, box_extra_type=None): self.env = Environment(name, general_type, box_extra_type) self.loader = DataLoader() self.provisioned = False self.tags = {} self.extra_vars = {'prudentia_dir': io.prudentia_python_dir()} self.load_tags() self.active_user = pwd.getpwuid(os.geteuid())[0] def boxes(self): return self.env.boxes.values() def get_box(self, box_name): b = self.env.get(box_name) if not b: print ('The box \'%s\' you entered does not exists.\n\n' \ 'After typing the command press Tab for box suggestions.\n' % box_name) return None else: return b def _show_current_vars(self): print('Current set variables:\n%s\n' % '\n'.join( [n + ' -> ' + str(v) for n, v in self.extra_vars.iteritems()])) def set_var(self, var, value): if var in self.extra_vars: print ('NOTICE: Variable \'{0}\' is already set to this value: \'{1}\' ' \ 'and it will be overwritten.'.format(var, self.extra_vars[var])) self.extra_vars[var] = value if provisioning.VERBOSITY > 0: print("Set \'{0}\' -> {1}\n".format(var, value)) def unset_var(self, var): if not var: print('Please provide a valid variable name to unset.\n') self._show_current_vars() elif var not in self.extra_vars: print( 'WARNING: Variable \'{0}\' is NOT present so cannot be unset.\n' .format(var)) self._show_current_vars() else: self.extra_vars.pop(var, None) print("Unset \'{0}\'\n".format(var)) def set_vault_password(self): vault_pwd = io.input_value('Ansible vault password', hidden=True) try: # Ansible 2.4 self.loader.set_vault_secrets(vault_pwd) except: # Ansible 2.3 self.loader.set_vault_password(vault_pwd) def load_vars(self, vars_file): if not vars_file: vars_file = io.input_path('path of the variables file') vars_dict = self.loader.load_from_file(vars_file) for key, value in vars_dict.items(): self.set_var(key, value) def add_box(self, box): self.env.add(box) self.load_tags(box) def load_tags(self, box=None): for b in [box] if box else self.boxes(): if not os.path.exists(b.playbook): print ('WARNING: Box \'{0}\' points to a NON existing playbook. ' \ 'Please `reconfigure` or `unregister` the box.\n'.format(b.name)) else: plays = Playbook.load( b.playbook, variable_manager=provisioning.get_variable_manager( self.loader), loader=self.loader).get_plays() all_tags = set() for p in plays: for block in p.compile(): for task in block.block: all_tags.update(task.tags) self.tags[b.name] = list(all_tags) def remove_box(self, box): if box.name in self.tags: self.tags.pop(box.name) return self.env.remove(box) def register(self): try: box = self.define_box() if box: self.add_box(box) print("\nBox %s added." % box) except Exception as ex: io.track_error('cannot add box', ex) @abstractmethod def define_box(self): pass def reconfigure(self, previous_box): try: box = self.redefine_box(previous_box) if box: self.remove_box(previous_box) self.add_box(box) print("\nBox %s reconfigured." % box) except Exception as ex: io.track_error('cannot reconfigure box', ex) @abstractmethod def redefine_box(self, previous_box): pass def unregister(self, box): self.remove_box(box) print("\nBox %s removed.\n" % box.name) def fetch_box_hosts(self, playbook): ds = self.loader.load_from_file(playbook) if ds: return ds[0][ 'hosts'] # a playbook is an array of plays we take the first one def suggest_name(self, hostname): if hostname not in self.env.boxes: return hostname else: return hostname + '-' + str(random.randint(0, 100)) def provision(self, box, tags): self.provisioned = provisioning.run_playbook( playbook_file=box.playbook, inventory_file=provisioning.generate_inventory(box), loader=self.loader, remote_user=box.get_remote_user(), remote_pass=box.get_remote_pwd(), transport=box.get_transport(), extra_vars=self.extra_vars, only_tags=tags) @staticmethod def verbose(value): if value: try: iv = int(value) except ValueError: iv = -1 if 0 <= iv <= 4: provisioning.VERBOSITY = iv else: print( 'Verbosity value \'{0}\' not allowed, should be a number between 0 and 4.' .format(value)) else: print('Current verbosity: {0}'.format(provisioning.VERBOSITY)) def facts(self, box, regex='*'): return provisioning.gather_facts(box, regex, self.loader)
def parse_yaml_from_file(filepath): dl = DataLoader() if hasattr(dl, 'set_vault_password'): dl.set_vault_password(DEFAULT_VAULT_PASSWORD) return dl.load_from_file(filepath)
def parse_yaml_from_file(filepath): dl = DataLoader() if hasattr(dl, 'set_vault_password'): dl.set_vault_password(DEFAULT_VAULT_PASSWORD) return dl.load_from_file(filepath)
class Runner(object): def __init__(self, playbook, run_data, private_key_file=None, verbosity=0, host_file=None): self.playbook = playbook self.run_data = run_data self.options = Options() self.options.output_file = playbook + '.result' self.options.private_key_file = private_key_file self.options.verbosity = verbosity self.options.connection = 'ssh' # Need a connection type "smart" or "ssh" #self.options.become = True self.options.become_method = 'sudo' self.options.become_user = '******' # Set global verbosity self.display = Display() self.display.verbosity = self.options.verbosity # Executor appears to have it's own # verbosity object/setting as well playbook_executor.verbosity = self.options.verbosity # Become Pass Needed if not logging in as user root #passwords = {'become_pass': become_pass} # Gets data from YAML/JSON files self.loader = DataLoader() try: self.loader.set_vault_password(os.environ['VAULT_PASS']) except KeyError: pass # All the variables from all the various places self.variable_manager = VariableManager() self.variable_manager.extra_vars = {} # self.run_data # Set inventory, using most of above objects if (host_file): self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=host_file) else: self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager) self.variable_manager.set_inventory(self.inventory) # Setup playbook executor, but don't run until run() called self.pbex = playbook_executor.PlaybookExecutor( playbooks=[playbook], inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords={}) def run(self): os.environ[ 'REQUESTS_CA_BUNDLE'] = '/usr/local/share/ca-certificates/local_certs.crt' callback = ResultCallback() self.pbex._tqm._stdout_callback = callback self.pbex.run() stats = self.pbex._tqm._stats # Test if success for record_logs run_success = True hosts = sorted(stats.processed.keys()) for h in hosts: t = stats.summarize(h) if t['unreachable'] > 0 or t['failures'] > 0: run_success = False #os.remove(self.hosts.name) return stats, callback.results
class InventoryCLI(CLI): ''' used to display or dump the configured inventory as Ansible sees it ''' ARGUMENTS = {'host': 'The name of a host to match in the inventory, relevant when using --list', 'group': 'The name of a group in the inventory, relevant when using --graph', } def __init__(self, args): super(InventoryCLI, self).__init__(args) self.vm = None self.loader = None self.inventory = None self._new_api = True def parse(self): self.parser = CLI.base_parser( usage='usage: %prog [options] [host|group]', epilog='Show Ansible inventory information, by default it uses the inventory script JSON format', inventory_opts=True, vault_opts=True ) # Actions action_group = optparse.OptionGroup(self.parser, "Actions", "One of following must be used on invocation, ONLY ONE!") action_group.add_option("--list", action="store_true", default=False, dest='list', help='Output all hosts info, works as inventory script') action_group.add_option("--host", action="store", default=None, dest='host', help='Output specific host info, works as inventory script') action_group.add_option("--graph", action="store_true", default=False, dest='graph', help='create inventory graph, if supplying pattern it must be a valid group name') self.parser.add_option_group(action_group) # Options self.parser.add_option("-y", "--yaml", action="store_true", default=False, dest='yaml', help='Use YAML format instead of default JSON, ignored for --graph') self.parser.add_option("--vars", action="store_true", default=False, dest='show_vars', help='Add vars to graph display, ignored unless used with --graph') super(InventoryCLI, self).parse() display.verbosity = self.options.verbosity self.validate_conflicts(vault_opts=True) # there can be only one! and, at least, one! used = 0 for opt in (self.options.list, self.options.host, self.options.graph): if opt: used += 1 if used == 0: raise AnsibleOptionsError("No action selected, at least one of --host, --graph or --list needs to be specified.") elif used > 1: raise AnsibleOptionsError("Conflicting options used, only one of --host, --graph or --list can be used at the same time.") # set host pattern to default if not supplied if len(self.args) > 0: self.options.pattern = self.args[0] else: self.options.pattern = 'all' 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) def dump(self, stuff): if self.options.yaml: import yaml from ansible.parsing.yaml.dumper import AnsibleDumper results = yaml.dump(stuff, Dumper=AnsibleDumper, default_flow_style=False) else: import json results = json.dumps(stuff, sort_keys=True, indent=4) return results def _get_host_variables(self, host): if self._new_api: hostvars = self.vm.get_vars(host=host) else: hostvars = self.vm.get_vars(self.loader, host=host) return hostvars def _get_group(self, gname): if self._new_api: group = self.inventory.groups.get(gname) else: group = self.inventory.get_group(gname) return group def _remove_internal(self, dump): for internal in INTERNAL_VARS: if internal in dump: del dump[internal] def _remove_empty(self, dump): # remove empty keys for x in ('hosts', 'vars', 'children'): if x in dump and not dump[x]: del dump[x] def _show_vars(self, dump, depth): result = [] self._remove_internal(dump) if self.options.show_vars: for (name, val) in sorted(dump.items()): result.append(self._graph_name('{%s = %s}' % (name, val), depth + 1)) return result def _graph_name(self, name, depth=0): if depth: name = " |" * (depth) + "--%s" % name return name def _graph_group(self, group, depth=0): result = [self._graph_name('@%s:' % group.name, depth)] depth = depth + 1 for kid in sorted(group.child_groups, key=attrgetter('name')): result.extend(self._graph_group(kid, depth)) if group.name != 'all': for host in sorted(group.hosts, key=attrgetter('name')): result.append(self._graph_name(host.name, depth)) result.extend(self._show_vars(host.get_vars(), depth)) result.extend(self._show_vars(group.get_vars(), depth)) return result def inventory_graph(self): start_at = self._get_group(self.options.pattern) if start_at: return '\n'.join(self._graph_group(start_at)) else: raise AnsibleOptionsError("Pattern must be valid group name when using --graph") def json_inventory(self, top): def format_group(group): results = {} results[group.name] = {} if group.name != 'all': results[group.name]['hosts'] = [h.name for h in sorted(group.hosts, key=attrgetter('name'))] results[group.name]['vars'] = group.get_vars() results[group.name]['children'] = [] for subgroup in sorted(group.child_groups, key=attrgetter('name')): results[group.name]['children'].append(subgroup.name) results.update(format_group(subgroup)) self._remove_empty(results[group.name]) return results results = format_group(top) # populate meta results['_meta'] = {'hostvars': {}} hosts = self.inventory.get_hosts() for host in hosts: results['_meta']['hostvars'][host.name] = self._get_host_variables(host=host) self._remove_internal(results['_meta']['hostvars'][host.name]) return results def yaml_inventory(self, top): seen = [] def format_group(group): results = {} # initialize group + vars results[group.name] = {} results[group.name]['vars'] = group.get_vars() # subgroups results[group.name]['children'] = {} for subgroup in sorted(group.child_groups, key=attrgetter('name')): if subgroup.name != 'all': results[group.name]['children'].update(format_group(subgroup)) # hosts for group results[group.name]['hosts'] = {} if group.name != 'all': for h in sorted(group.hosts, key=attrgetter('name')): myvars = {} if h.name not in seen: # avoid defining host vars more than once seen.append(h.name) myvars = self._get_host_variables(host=h) self._remove_internal(myvars) results[group.name]['hosts'][h.name] = myvars self._remove_empty(results[group.name]) return results return format_group(top)
def run(self): super(PlaybookCLI, self).run() # Note: slightly wrong, this is written so that implicit localhost # Manage passwords sshpass = None becomepass = None vault_pass = None passwords = {} # 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 = DataLoader() if self.options.vault_password_file: # read vault_pass from a file vault_pass = CLI.read_vault_password_file(self.options.vault_password_file, loader=loader) loader.set_vault_password(vault_pass) elif self.options.ask_vault_pass: vault_pass = self.ask_vault_passwords()[0] loader.set_vault_password(vault_pass) # 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) # create the variable manager, which will be shared throughout # the code, ensuring a consistent view of global variables variable_manager = VariableManager() variable_manager.extra_vars = load_extra_vars(loader=loader, options=self.options) variable_manager.options_vars = load_options_vars(self.options) # create the inventory, and filter it based on the subset specified (if any) inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list=self.options.inventory) variable_manager.set_inventory(inventory) # (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") # 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']): 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(loader=loader, 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
def run(self): super(PlaybookCLI, self).run() # Note: slightly wrong, this is written so that implicit localhost # Manage passwords sshpass = None becomepass = None vault_pass = None passwords = {} # 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 = DataLoader() if self.options.vault_password_file: # read vault_pass from a file vault_pass = CLI.read_vault_password_file( self.options.vault_password_file, loader=loader) loader.set_vault_password(vault_pass) elif self.options.ask_vault_pass: vault_pass = self.ask_vault_passwords()[0] loader.set_vault_password(vault_pass) # create the variable manager, which will be shared throughout # the code, ensuring a consistent view of global variables variable_manager = VariableManager() variable_manager.extra_vars = load_extra_vars(loader=loader, options=self.options) variable_manager.options_vars = load_options_vars(self.options) # create the inventory, and filter it based on the subset specified (if any) inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list=self.options.inventory) variable_manager.set_inventory(inventory) # (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: for host in inventory.list_hosts(): variable_manager.clear_facts(host) # 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']): 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(loader=loader, 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
class Base(): def __init__(self, exec_mode, work_name, username, options_dict, describe, mongoclient=None): ''' ansible的基类,为adhoc、playbook等父类 :parm: exec_mode:ansible工作类型,接受adhoc、palybook等 work_name:该任务的名称,用于日志 username:执行者 options_dict:该任务的特定设置 mongoclient:初始化mongo连接类 ''' self.logger = logging.getLogger("ansible") if exec_mode in ('adhoc', 'playbook'): self.exec_mode = exec_mode else: self.logger.warn( '正在准备执行ansible任务,准备工作失败,原因:参数exec_mode必须是adhoc或者是playbook') return (False, '参数exec_mode必须是adhoc或者是playbook') if isinstance(work_name, str) and work_name: self.work_name = work_name else: self.logger.warn('正在准备执行ansible任务,准备工作失败,原因:参数work_name必须是非字符串') return (False, '参数work_name必须是非字符串') if isinstance(username, str) and username: self.username = username else: self.logger.warn('正在准备执行ansible任务,准备工作失败,原因:参数username必须是非字符串') return (False, '参数username必须是非字符串') if isinstance(options_dict, dict) and options_dict: self.options_dict = options_dict else: self.logger.warn('正在准备执行ansible任务,准备工作失败,原因:参数options_dict必须是非空字典') return (False, '参数options_dict必须是非空字典') if mongoclient is None: self.mongoclient = Op_Mongo() else: self.mongoclient = mongoclient self.work_uuid = str(uuid.uuid4()) self.log_router = Routing_Logging() self.describe = describe if exec_mode == 'adhoc': self.log_prefix = '正在执行用户' + username + '的名为' + work_name + 'uuid为' + self.work_uuid + '的ansible临时任务,' else: self.log_prefix = '正在执行用户' + username + '的名为' + work_name + 'uuid为' + self.work_uuid + '的ansible-playbook任务,' # 下面是加载和初始化相关类 self._parse_options() self.passwords = { 'conn_pass': self.options.ask_pass, 'become_pass': self.options.become_ask_pass } # 设置passwords self.inventory_file = self.options_dict.get('inventory', '') self.loader = DataLoader() self._get_vault_pwd() self.variable_manager = VariableManager() self.variable_manager.extra_vars = load_extra_vars( loader=self.loader, options=self.options) self.variable_manager.options_vars = load_options_vars(self.options) self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=self.inventory_file) self.variable_manager.set_inventory(self.inventory) self.inventory.subset(self.options.subset) def _get_vault_pwd(self): ''' 获取vault密码,并初始化vault密码 ''' vault_pwd_file = self.options_dict.get('vault_password_file', False) ask_vault_pass = self.options_dict.get('ask_vault_pass', False) if ask_vault_pass: vault_password = ask_vault_pass else: if vault_pwd_file: this_path = os.path.realpath( os.path.expanduser(vault_pwd_file)) if os.path.exists(this_path): mode = self.loader.is_executable(this_path) result = read_file(this_path, mode=mode, sprfmt=b'\r\n', outfmt='bytes') if result[0]: vault_password = result[1] else: vault_password = None else: vault_password = None else: vault_password = None if vault_password: b_vault_password = string2bytes(vault_password) self.loader.set_vault_password(b_vault_password) self.vault_password = vault_password else: self.vault_password = None def _parse_options(self): ''' 获取options字典 ''' keys_list = [] values_list = [] for key, value in self.options_dict.items(): keys_list.append(key) values_list.append(value) Options = namedtuple('Options', keys_list) self.options = Options._make(values_list) def _show_protectfield(self): protectfield_list = [ 'ask_pass', 'ask_su_pass', 'ask_sudo_pass', 'ask_vault_pass', 'become_ask_pass', 'new_vault_password_file', 'vault_password_file', 'private_key_file', ] log_options = {} for field in self.options_dict: if field in protectfield_list: log_options[field] = '***hidden***' else: log_options[field] = self.options_dict[field] return log_options