Fixing some v2 inventory bugs

pull/10122/head
James Cammarata 10 years ago
parent b6a34518ad
commit 4d9bf37afa

@ -32,15 +32,9 @@ from ansible.inventory.dir import InventoryDirectory
from ansible.inventory.group import Group from ansible.inventory.group import Group
from ansible.inventory.host import Host from ansible.inventory.host import Host
from ansible.plugins import vars_loader from ansible.plugins import vars_loader
from ansible.utils.path import is_executable
from ansible.utils.vars import combine_vars 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): class Inventory(object):
""" """
Host inventory for ansible. Host inventory for ansible.
@ -108,7 +102,7 @@ class Inventory(object):
if os.path.isdir(host_list): if os.path.isdir(host_list):
# Ensure basedir is inside the directory # Ensure basedir is inside the directory
self.host_list = os.path.join(self.host_list, "") 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() self.groups = self.parser.groups.values()
else: else:
# check to see if the specified file starts with a # check to see if the specified file starts with a
@ -124,10 +118,9 @@ class Inventory(object):
except: except:
pass pass
# FIXME: utils is_executable
if is_executable(host_list): if is_executable(host_list):
try: try:
self.parser = InventoryScript(filename=host_list) self.parser = InventoryScript(loader=self._loader, filename=host_list)
self.groups = self.parser.groups.values() self.groups = self.parser.groups.values()
except: except:
if not shebang_present: if not shebang_present:

@ -19,18 +19,21 @@
############################################# #############################################
import os 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.host import Host
from ansible.inventory.group import Group from ansible.inventory.group import Group
from ansible.inventory.ini import InventoryParser from ansible.inventory.ini import InventoryParser
from ansible.inventory.script import InventoryScript from ansible.inventory.script import InventoryScript
from ansible import utils from ansible.utils.path import is_executable
from ansible import errors from ansible.utils.vars import combine_vars
class InventoryDirectory(object): class InventoryDirectory(object):
''' Host inventory parser for ansible using a directory of inventories. ''' ''' 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 = os.listdir(filename)
self.names.sort() self.names.sort()
self.directory = filename self.directory = filename
@ -38,10 +41,12 @@ class InventoryDirectory(object):
self.hosts = {} self.hosts = {}
self.groups = {} self.groups = {}
self._loader = loader
for i in self.names: for i in self.names:
# Skip files that end with certain extensions or characters # 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 continue
# Skip hidden files # Skip hidden files
if i.startswith('.') and not i.startswith('./'): if i.startswith('.') and not i.startswith('./'):
@ -51,9 +56,9 @@ class InventoryDirectory(object):
continue continue
fullpath = os.path.join(self.directory, i) fullpath = os.path.join(self.directory, i)
if os.path.isdir(fullpath): if os.path.isdir(fullpath):
parser = InventoryDirectory(filename=fullpath) parser = InventoryDirectory(loader=loader, filename=fullpath)
elif utils.is_executable(fullpath): elif is_executable(fullpath):
parser = InventoryScript(filename=fullpath) parser = InventoryScript(loader=loader, filename=fullpath)
else: else:
parser = InventoryParser(filename=fullpath) parser = InventoryParser(filename=fullpath)
self.parsers.append(parser) self.parsers.append(parser)
@ -196,7 +201,7 @@ class InventoryDirectory(object):
self.groups[newparent.name].add_child_group(group) self.groups[newparent.name].add_child_group(group)
# variables # variables
group.vars = utils.combine_vars(group.vars, newgroup.vars) group.vars = combine_vars(group.vars, newgroup.vars)
def _merge_hosts(self,host, newhost): def _merge_hosts(self,host, newhost):
""" Merge all of instance newhost into host """ """ Merge all of instance newhost into host """
@ -218,7 +223,7 @@ class InventoryDirectory(object):
self.groups[newgroup.name].add_host(host) self.groups[newgroup.name].add_host(host)
# variables # variables
host.vars = utils.combine_vars(host.vars, newhost.vars) host.vars = combine_vars(host.vars, newhost.vars)
def get_host_variables(self, host): def get_host_variables(self, host):
""" Gets additional host variables from all inventories """ """ Gets additional host variables from all inventories """

@ -34,7 +34,7 @@ class InventoryParser(object):
""" """
def __init__(self, filename=C.DEFAULT_HOST_LIST): def __init__(self, filename=C.DEFAULT_HOST_LIST):
self.filename = filename
with open(filename) as fh: with open(filename) as fh:
self.lines = fh.readlines() self.lines = fh.readlines()
self.groups = {} self.groups = {}
@ -142,7 +142,7 @@ class InventoryParser(object):
try: try:
(k,v) = t.split("=", 1) (k,v) = t.split("=", 1)
except ValueError, e: 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': if k == 'ansible_ssh_host':
host.ipv4_address = self._parse_value(v) host.ipv4_address = self._parse_value(v)
else: else:

@ -19,19 +19,21 @@
import os import os
import subprocess 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.host import Host
from ansible.inventory.group import Group from ansible.inventory.group import Group
from ansible.module_utils.basic import json_dict_bytes_to_unicode 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. ''' ''' 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 # Support inventory scripts that are not prefixed with some
# path information but happen to be in the current working # path information but happen to be in the current working
@ -41,11 +43,11 @@ class InventoryScript(object):
try: try:
sp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) sp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except OSError, e: 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() (stdout, stderr) = sp.communicate()
if sp.returncode != 0: 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 self.data = stdout
# see comment about _meta below # see comment about _meta below
@ -58,7 +60,7 @@ class InventoryScript(object):
all_hosts = {} all_hosts = {}
# not passing from_remote because data from CMDB is trusted # 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) self.raw = json_dict_bytes_to_unicode(self.raw)
all = Group('all') all = Group('all')
@ -68,7 +70,7 @@ class InventoryScript(object):
if 'failed' in self.raw: if 'failed' in self.raw:
sys.stderr.write(err + "\n") 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(): for (group_name, data) in self.raw.items():
@ -97,7 +99,7 @@ class InventoryScript(object):
if 'hosts' in data: if 'hosts' in data:
if not isinstance(data['hosts'], list): 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)) "data for the host list:\n %s" % (group_name, data))
for hostname in data['hosts']: for hostname in data['hosts']:
@ -108,7 +110,7 @@ class InventoryScript(object):
if 'vars' in data: if 'vars' in data:
if not isinstance(data['vars'], dict): 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)) "data for variables:\n %s" % (group_name, data))
for k, v in data['vars'].iteritems(): for k, v in data['vars'].iteritems():
@ -143,12 +145,12 @@ class InventoryScript(object):
try: try:
sp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) sp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except OSError, e: 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() (out, err) = sp.communicate()
if out.strip() == '': if out.strip() == '':
return dict() return dict()
try: try:
return json_dict_bytes_to_unicode(utils.parse_json(out)) return json_dict_bytes_to_unicode(self._loader.load(out))
except ValueError: 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))

@ -66,7 +66,6 @@ class DataLoader():
a JSON or YAML string. a JSON or YAML string.
''' '''
#print("in load, data is: %s (%s)" % (data, type(data)))
try: try:
# we first try to load this data as JSON # we first try to load this data as JSON
return json.loads(data) return json.loads(data)
@ -109,8 +108,6 @@ class DataLoader():
def _safe_load(self, stream, file_name=None): def _safe_load(self, stream, file_name=None):
''' Implements yaml.safe_load(), except using our custom loader class. ''' ''' 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) loader = AnsibleLoader(stream, file_name)
try: try:
return loader.get_single_data() return loader.get_single_data()

@ -0,0 +1,26 @@
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
#
# 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 <http://www.gnu.org/licenses/>.
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])
Loading…
Cancel
Save