mirror of
https://github.com/thegeeklab/ansible-later.git
synced 2024-11-26 23:00:36 +00:00
272 lines
9.7 KiB
Python
272 lines
9.7 KiB
Python
|
# (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/>.
|
||
|
|
||
|
#############################################
|
||
|
from __future__ import (absolute_import, division, print_function)
|
||
|
__metaclass__ = type
|
||
|
|
||
|
import sys
|
||
|
|
||
|
from ansible import constants as C
|
||
|
from ansible.errors import AnsibleError
|
||
|
from ansible.inventory.group import Group
|
||
|
from ansible.inventory.host import Host
|
||
|
from ansible.module_utils.six import iteritems
|
||
|
from ansible.utils.vars import combine_vars
|
||
|
from ansible.utils.path import basedir
|
||
|
|
||
|
try:
|
||
|
from __main__ import display
|
||
|
except ImportError:
|
||
|
from ansible.utils.display import Display
|
||
|
display = Display()
|
||
|
|
||
|
|
||
|
class InventoryData(object):
|
||
|
"""
|
||
|
Holds inventory data (host and group objects).
|
||
|
Using it's methods should guarantee expected relationships and data.
|
||
|
"""
|
||
|
|
||
|
def __init__(self):
|
||
|
|
||
|
# the inventory object holds a list of groups
|
||
|
self.groups = {}
|
||
|
self.hosts = {}
|
||
|
|
||
|
# provides 'groups' magic var, host object has group_names
|
||
|
self._groups_dict_cache = {}
|
||
|
|
||
|
# current localhost, implicit or explicit
|
||
|
self.localhost = None
|
||
|
|
||
|
self.current_source = None
|
||
|
|
||
|
# Always create the 'all' and 'ungrouped' groups,
|
||
|
for group in ('all', 'ungrouped'):
|
||
|
self.add_group(group)
|
||
|
self.add_child('all', 'ungrouped')
|
||
|
|
||
|
def serialize(self):
|
||
|
self._groups_dict_cache = None
|
||
|
data = {
|
||
|
'groups': self.groups,
|
||
|
'hosts': self.hosts,
|
||
|
'local': self.localhost,
|
||
|
'source': self.current_source,
|
||
|
}
|
||
|
return data
|
||
|
|
||
|
def deserialize(self, data):
|
||
|
self._groups_dict_cache = {}
|
||
|
self.hosts = data.get('hosts')
|
||
|
self.groups = data.get('groups')
|
||
|
self.localhost = data.get('local')
|
||
|
self.current_source = data.get('source')
|
||
|
|
||
|
def _create_implicit_localhost(self, pattern):
|
||
|
|
||
|
if self.localhost:
|
||
|
new_host = self.localhost
|
||
|
else:
|
||
|
new_host = Host(pattern)
|
||
|
|
||
|
new_host.address = "127.0.0.1"
|
||
|
new_host.implicit = True
|
||
|
|
||
|
# set localhost defaults
|
||
|
py_interp = sys.executable
|
||
|
if not py_interp:
|
||
|
# sys.executable is not set in some cornercases. see issue #13585
|
||
|
py_interp = '/usr/bin/python'
|
||
|
display.warning('Unable to determine python interpreter from sys.executable. Using /usr/bin/python default. '
|
||
|
'You can correct this by setting ansible_python_interpreter for localhost')
|
||
|
new_host.set_variable("ansible_python_interpreter", py_interp)
|
||
|
new_host.set_variable("ansible_connection", 'local')
|
||
|
|
||
|
self.localhost = new_host
|
||
|
|
||
|
return new_host
|
||
|
|
||
|
def reconcile_inventory(self):
|
||
|
''' Ensure inventory basic rules, run after updates '''
|
||
|
|
||
|
display.debug('Reconcile groups and hosts in inventory.')
|
||
|
self.current_source = None
|
||
|
|
||
|
group_names = set()
|
||
|
# set group vars from group_vars/ files and vars plugins
|
||
|
for g in self.groups:
|
||
|
group = self.groups[g]
|
||
|
group_names.add(group.name)
|
||
|
|
||
|
# ensure all groups inherit from 'all'
|
||
|
if group.name != 'all' and not group.get_ancestors():
|
||
|
self.add_child('all', group.name)
|
||
|
|
||
|
host_names = set()
|
||
|
# get host vars from host_vars/ files and vars plugins
|
||
|
for host in self.hosts.values():
|
||
|
host_names.add(host.name)
|
||
|
|
||
|
mygroups = host.get_groups()
|
||
|
|
||
|
if self.groups['ungrouped'] in mygroups:
|
||
|
# clear ungrouped of any incorrectly stored by parser
|
||
|
if set(mygroups).difference(set([self.groups['all'], self.groups['ungrouped']])):
|
||
|
self.groups['ungrouped'].remove_host(host)
|
||
|
|
||
|
elif not host.implicit:
|
||
|
# add ungrouped hosts to ungrouped, except implicit
|
||
|
length = len(mygroups)
|
||
|
if length == 0 or (length == 1 and self.groups['all'] in mygroups):
|
||
|
self.add_child('ungrouped', host.name)
|
||
|
|
||
|
# special case for implicit hosts
|
||
|
if host.implicit:
|
||
|
host.vars = combine_vars(self.groups['all'].get_vars(), host.vars)
|
||
|
|
||
|
# warn if overloading identifier as both group and host
|
||
|
for conflict in group_names.intersection(host_names):
|
||
|
display.warning("Found both group and host with same name: %s" % conflict)
|
||
|
|
||
|
self._groups_dict_cache = {}
|
||
|
|
||
|
def get_host(self, hostname):
|
||
|
''' fetch host object using name deal with implicit localhost '''
|
||
|
|
||
|
matching_host = self.hosts.get(hostname, None)
|
||
|
|
||
|
# if host is not in hosts dict
|
||
|
if matching_host is None and hostname in C.LOCALHOST:
|
||
|
# might need to create implicit localhost
|
||
|
matching_host = self._create_implicit_localhost(hostname)
|
||
|
|
||
|
return matching_host
|
||
|
|
||
|
def add_group(self, group):
|
||
|
''' adds a group to inventory if not there already '''
|
||
|
|
||
|
if group:
|
||
|
if group not in self.groups:
|
||
|
g = Group(group)
|
||
|
self.groups[group] = g
|
||
|
self._groups_dict_cache = {}
|
||
|
display.debug("Added group %s to inventory" % group)
|
||
|
else:
|
||
|
display.debug("group %s already in inventory" % group)
|
||
|
else:
|
||
|
raise AnsibleError("Invalid empty/false group name provided: %s" % group)
|
||
|
|
||
|
def remove_group(self, group):
|
||
|
|
||
|
if group in self.groups:
|
||
|
del self.groups[group]
|
||
|
display.debug("Removed group %s from inventory" % group)
|
||
|
self._groups_dict_cache = {}
|
||
|
|
||
|
for host in self.hosts:
|
||
|
h = self.hosts[host]
|
||
|
h.remove_group(group)
|
||
|
|
||
|
def add_host(self, host, group=None, port=None):
|
||
|
''' adds a host to inventory and possibly a group if not there already '''
|
||
|
|
||
|
if host:
|
||
|
g = None
|
||
|
if group:
|
||
|
if group in self.groups:
|
||
|
g = self.groups[group]
|
||
|
else:
|
||
|
raise AnsibleError("Could not find group %s in inventory" % group)
|
||
|
|
||
|
if host not in self.hosts:
|
||
|
h = Host(host, port)
|
||
|
self.hosts[host] = h
|
||
|
if self.current_source: # set to 'first source' in which host was encountered
|
||
|
self.set_variable(host, 'inventory_file', self.current_source)
|
||
|
self.set_variable(host, 'inventory_dir', basedir(self.current_source))
|
||
|
else:
|
||
|
self.set_variable(host, 'inventory_file', None)
|
||
|
self.set_variable(host, 'inventory_dir', None)
|
||
|
display.debug("Added host %s to inventory" % (host))
|
||
|
|
||
|
# set default localhost from inventory to avoid creating an implicit one. Last localhost defined 'wins'.
|
||
|
if host in C.LOCALHOST:
|
||
|
if self.localhost is None:
|
||
|
self.localhost = self.hosts[host]
|
||
|
display.vvvv("Set default localhost to %s" % h)
|
||
|
else:
|
||
|
display.warning("A duplicate localhost-like entry was found (%s). First found localhost was %s" % (h, self.localhost.name))
|
||
|
else:
|
||
|
h = self.hosts[host]
|
||
|
|
||
|
if g:
|
||
|
g.add_host(h)
|
||
|
self._groups_dict_cache = {}
|
||
|
display.debug("Added host %s to group %s" % (host, group))
|
||
|
else:
|
||
|
raise AnsibleError("Invalid empty host name provided: %s" % host)
|
||
|
|
||
|
def remove_host(self, host):
|
||
|
|
||
|
if host in self.hosts:
|
||
|
del self.hosts[host]
|
||
|
|
||
|
for group in self.groups:
|
||
|
g = self.groups[group]
|
||
|
g.remove_host(host)
|
||
|
|
||
|
def set_variable(self, entity, varname, value):
|
||
|
''' sets a varible for an inventory object '''
|
||
|
|
||
|
if entity in self.groups:
|
||
|
inv_object = self.groups[entity]
|
||
|
elif entity in self.hosts:
|
||
|
inv_object = self.hosts[entity]
|
||
|
else:
|
||
|
raise AnsibleError("Could not identify group or host named %s" % entity)
|
||
|
|
||
|
inv_object.set_variable(varname, value)
|
||
|
display.debug('set %s for %s' % (varname, entity))
|
||
|
|
||
|
def add_child(self, group, child):
|
||
|
''' Add host or group to group '''
|
||
|
|
||
|
if group in self.groups:
|
||
|
g = self.groups[group]
|
||
|
if child in self.groups:
|
||
|
g.add_child_group(self.groups[child])
|
||
|
elif child in self.hosts:
|
||
|
g.add_host(self.hosts[child])
|
||
|
else:
|
||
|
raise AnsibleError("%s is not a known host nor group" % child)
|
||
|
self._groups_dict_cache = {}
|
||
|
display.debug('Group %s now contains %s' % (group, child))
|
||
|
else:
|
||
|
raise AnsibleError("%s is not a known group" % group)
|
||
|
|
||
|
def get_groups_dict(self):
|
||
|
"""
|
||
|
We merge a 'magic' var 'groups' with group name keys and hostname list values into every host variable set. Cache for speed.
|
||
|
"""
|
||
|
if not self._groups_dict_cache:
|
||
|
for (group_name, group) in iteritems(self.groups):
|
||
|
self._groups_dict_cache[group_name] = [h.name for h in group.get_hosts()]
|
||
|
|
||
|
return self._groups_dict_cache
|