From 4d9bf37afa257cfd94aef53c624c724207ab883e Mon Sep 17 00:00:00 2001 From: James Cammarata Date: Wed, 28 Jan 2015 13:17:56 -0600 Subject: [PATCH] Fixing some v2 inventory bugs --- v2/ansible/inventory/__init__.py | 13 +++---------- v2/ansible/inventory/dir.py | 25 +++++++++++++++---------- v2/ansible/inventory/ini.py | 4 ++-- v2/ansible/inventory/script.py | 32 +++++++++++++++++--------------- v2/ansible/parsing/__init__.py | 3 --- v2/ansible/utils/path.py | 26 ++++++++++++++++++++++++++ 6 files changed, 63 insertions(+), 40 deletions(-) create mode 100644 v2/ansible/utils/path.py diff --git a/v2/ansible/inventory/__init__.py b/v2/ansible/inventory/__init__.py index 56c8c7ced0..c8e3cddeba 100644 --- a/v2/ansible/inventory/__init__.py +++ b/v2/ansible/inventory/__init__.py @@ -32,15 +32,9 @@ from ansible.inventory.dir import InventoryDirectory from ansible.inventory.group import Group from ansible.inventory.host import Host from ansible.plugins import vars_loader +from ansible.utils.path import is_executable from ansible.utils.vars import combine_vars -# FIXME: these defs need to be somewhere else -def is_executable(path): - '''is the given path executable?''' - return (stat.S_IXUSR & os.stat(path)[stat.ST_MODE] - or stat.S_IXGRP & os.stat(path)[stat.ST_MODE] - or stat.S_IXOTH & os.stat(path)[stat.ST_MODE]) - class Inventory(object): """ Host inventory for ansible. @@ -108,7 +102,7 @@ class Inventory(object): if os.path.isdir(host_list): # Ensure basedir is inside the directory self.host_list = os.path.join(self.host_list, "") - self.parser = InventoryDirectory(filename=host_list) + self.parser = InventoryDirectory(loader=self._loader, filename=host_list) self.groups = self.parser.groups.values() else: # check to see if the specified file starts with a @@ -124,10 +118,9 @@ class Inventory(object): except: pass - # FIXME: utils is_executable if is_executable(host_list): try: - self.parser = InventoryScript(filename=host_list) + self.parser = InventoryScript(loader=self._loader, filename=host_list) self.groups = self.parser.groups.values() except: if not shebang_present: diff --git a/v2/ansible/inventory/dir.py b/v2/ansible/inventory/dir.py index 9ac23fff89..52f7af8b53 100644 --- a/v2/ansible/inventory/dir.py +++ b/v2/ansible/inventory/dir.py @@ -19,18 +19,21 @@ ############################################# import os -import ansible.constants as C + +from ansible import constants as C +from ansible.errors import * + from ansible.inventory.host import Host from ansible.inventory.group import Group from ansible.inventory.ini import InventoryParser from ansible.inventory.script import InventoryScript -from ansible import utils -from ansible import errors +from ansible.utils.path import is_executable +from ansible.utils.vars import combine_vars class InventoryDirectory(object): ''' Host inventory parser for ansible using a directory of inventories. ''' - def __init__(self, filename=C.DEFAULT_HOST_LIST): + def __init__(self, loader, filename=C.DEFAULT_HOST_LIST): self.names = os.listdir(filename) self.names.sort() self.directory = filename @@ -38,10 +41,12 @@ class InventoryDirectory(object): self.hosts = {} self.groups = {} + self._loader = loader + for i in self.names: # Skip files that end with certain extensions or characters - if any(i.endswith(ext) for ext in ("~", ".orig", ".bak", ".ini", ".retry", ".pyc", ".pyo")): + if any(i.endswith(ext) for ext in ("~", ".orig", ".bak", ".ini", ".cfg", ".retry", ".pyc", ".pyo")): continue # Skip hidden files if i.startswith('.') and not i.startswith('./'): @@ -51,9 +56,9 @@ class InventoryDirectory(object): continue fullpath = os.path.join(self.directory, i) if os.path.isdir(fullpath): - parser = InventoryDirectory(filename=fullpath) - elif utils.is_executable(fullpath): - parser = InventoryScript(filename=fullpath) + parser = InventoryDirectory(loader=loader, filename=fullpath) + elif is_executable(fullpath): + parser = InventoryScript(loader=loader, filename=fullpath) else: parser = InventoryParser(filename=fullpath) self.parsers.append(parser) @@ -196,7 +201,7 @@ class InventoryDirectory(object): self.groups[newparent.name].add_child_group(group) # variables - group.vars = utils.combine_vars(group.vars, newgroup.vars) + group.vars = combine_vars(group.vars, newgroup.vars) def _merge_hosts(self,host, newhost): """ Merge all of instance newhost into host """ @@ -218,7 +223,7 @@ class InventoryDirectory(object): self.groups[newgroup.name].add_host(host) # variables - host.vars = utils.combine_vars(host.vars, newhost.vars) + host.vars = combine_vars(host.vars, newhost.vars) def get_host_variables(self, host): """ Gets additional host variables from all inventories """ diff --git a/v2/ansible/inventory/ini.py b/v2/ansible/inventory/ini.py index ef3f162aa3..075701c056 100644 --- a/v2/ansible/inventory/ini.py +++ b/v2/ansible/inventory/ini.py @@ -34,7 +34,7 @@ class InventoryParser(object): """ def __init__(self, filename=C.DEFAULT_HOST_LIST): - + self.filename = filename with open(filename) as fh: self.lines = fh.readlines() self.groups = {} @@ -142,7 +142,7 @@ class InventoryParser(object): try: (k,v) = t.split("=", 1) except ValueError, e: - raise AnsibleError("Invalid ini entry: %s - %s" % (t, str(e))) + raise AnsibleError("Invalid ini entry in %s: %s - %s" % (self.filename, t, str(e))) if k == 'ansible_ssh_host': host.ipv4_address = self._parse_value(v) else: diff --git a/v2/ansible/inventory/script.py b/v2/ansible/inventory/script.py index 9b8d72de41..13b53a24f5 100644 --- a/v2/ansible/inventory/script.py +++ b/v2/ansible/inventory/script.py @@ -19,19 +19,21 @@ import os import subprocess -import ansible.constants as C +import sys + +from ansible import constants as C +from ansible.errors import * from ansible.inventory.host import Host from ansible.inventory.group import Group from ansible.module_utils.basic import json_dict_bytes_to_unicode -from ansible import utils -from ansible import errors -import sys -class InventoryScript(object): +class InventoryScript: ''' Host inventory parser for ansible using external inventory scripts. ''' - def __init__(self, filename=C.DEFAULT_HOST_LIST): + def __init__(self, loader, filename=C.DEFAULT_HOST_LIST): + + self._loader = loader # Support inventory scripts that are not prefixed with some # path information but happen to be in the current working @@ -41,11 +43,11 @@ class InventoryScript(object): try: sp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError, e: - raise errors.AnsibleError("problem running %s (%s)" % (' '.join(cmd), e)) + raise AnsibleError("problem running %s (%s)" % (' '.join(cmd), e)) (stdout, stderr) = sp.communicate() if sp.returncode != 0: - raise errors.AnsibleError("Inventory script (%s) had an execution error: %s " % (filename,stderr)) + raise AnsibleError("Inventory script (%s) had an execution error: %s " % (filename,stderr)) self.data = stdout # see comment about _meta below @@ -58,7 +60,7 @@ class InventoryScript(object): all_hosts = {} # not passing from_remote because data from CMDB is trusted - self.raw = utils.parse_json(self.data) + self.raw = self._loader.load(self.data) self.raw = json_dict_bytes_to_unicode(self.raw) all = Group('all') @@ -68,7 +70,7 @@ class InventoryScript(object): if 'failed' in self.raw: sys.stderr.write(err + "\n") - raise errors.AnsibleError("failed to parse executable inventory script results: %s" % self.raw) + raise AnsibleError("failed to parse executable inventory script results: %s" % self.raw) for (group_name, data) in self.raw.items(): @@ -97,7 +99,7 @@ class InventoryScript(object): if 'hosts' in data: if not isinstance(data['hosts'], list): - raise errors.AnsibleError("You defined a group \"%s\" with bad " + raise AnsibleError("You defined a group \"%s\" with bad " "data for the host list:\n %s" % (group_name, data)) for hostname in data['hosts']: @@ -108,7 +110,7 @@ class InventoryScript(object): if 'vars' in data: if not isinstance(data['vars'], dict): - raise errors.AnsibleError("You defined a group \"%s\" with bad " + raise AnsibleError("You defined a group \"%s\" with bad " "data for variables:\n %s" % (group_name, data)) for k, v in data['vars'].iteritems(): @@ -143,12 +145,12 @@ class InventoryScript(object): try: sp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError, e: - raise errors.AnsibleError("problem running %s (%s)" % (' '.join(cmd), e)) + raise AnsibleError("problem running %s (%s)" % (' '.join(cmd), e)) (out, err) = sp.communicate() if out.strip() == '': return dict() try: - return json_dict_bytes_to_unicode(utils.parse_json(out)) + return json_dict_bytes_to_unicode(self._loader.load(out)) except ValueError: - raise errors.AnsibleError("could not parse post variable response: %s, %s" % (cmd, out)) + raise AnsibleError("could not parse post variable response: %s, %s" % (cmd, out)) diff --git a/v2/ansible/parsing/__init__.py b/v2/ansible/parsing/__init__.py index 82679ad9e8..861ab57acd 100644 --- a/v2/ansible/parsing/__init__.py +++ b/v2/ansible/parsing/__init__.py @@ -66,7 +66,6 @@ class DataLoader(): a JSON or YAML string. ''' - #print("in load, data is: %s (%s)" % (data, type(data))) try: # we first try to load this data as JSON return json.loads(data) @@ -109,8 +108,6 @@ class DataLoader(): def _safe_load(self, stream, file_name=None): ''' Implements yaml.safe_load(), except using our custom loader class. ''' - #print("stream is: %s" % stream) - #print("file name is: %s" % file_name) loader = AnsibleLoader(stream, file_name) try: return loader.get_single_data() diff --git a/v2/ansible/utils/path.py b/v2/ansible/utils/path.py new file mode 100644 index 0000000000..ad6e054727 --- /dev/null +++ b/v2/ansible/utils/path.py @@ -0,0 +1,26 @@ +# (c) 2012-2014, Michael DeHaan +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +import os +import stat + +__all__ = ['is_executable'] + +def is_executable(path): + '''is the given path executable?''' + return (stat.S_IXUSR & os.stat(path)[stat.ST_MODE] or stat.S_IXGRP & os.stat(path)[stat.ST_MODE] or stat.S_IXOTH & os.stat(path)[stat.ST_MODE]) +