Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
ansible
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
ansible
Commits
4e5eb755
Commit
4e5eb755
authored
Jul 10, 2014
by
James Cammarata
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'svg_and_inventory_refactor' into devel
parents
6d24f437
aa261bdd
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
429 additions
and
247 deletions
+429
-247
bin/ansible
+1
-1
bin/ansible-playbook
+5
-8
lib/ansible/inventory/__init__.py
+0
-0
lib/ansible/inventory/dir.py
+163
-23
lib/ansible/inventory/group.py
+19
-2
lib/ansible/inventory/ini.py
+8
-2
lib/ansible/inventory/script.py
+7
-3
lib/ansible/inventory/vars_plugins/group_vars.py
+0
-195
lib/ansible/inventory/vars_plugins/noop.py
+48
-0
lib/ansible/playbook/__init__.py
+4
-0
lib/ansible/utils/__init__.py
+121
-12
test/units/TestInventory.py
+53
-1
No files found.
bin/ansible
View file @
4e5eb755
...
@@ -136,7 +136,7 @@ class Cli(object):
...
@@ -136,7 +136,7 @@ class Cli(object):
if
not
options
.
ask_vault_pass
:
if
not
options
.
ask_vault_pass
:
vault_pass
=
tmp_vault_pass
vault_pass
=
tmp_vault_pass
inventory_manager
=
inventory
.
Inventory
(
options
.
inventory
)
inventory_manager
=
inventory
.
Inventory
(
options
.
inventory
,
vault_password
=
vault_pass
)
if
options
.
subset
:
if
options
.
subset
:
inventory_manager
.
subset
(
options
.
subset
)
inventory_manager
.
subset
(
options
.
subset
)
hosts
=
inventory_manager
.
list_hosts
(
pattern
)
hosts
=
inventory_manager
.
list_hosts
(
pattern
)
...
...
bin/ansible-playbook
View file @
4e5eb755
...
@@ -100,11 +100,6 @@ def main(args):
...
@@ -100,11 +100,6 @@ def main(args):
if
(
options
.
ask_vault_pass
and
options
.
vault_password_file
):
if
(
options
.
ask_vault_pass
and
options
.
vault_password_file
):
parser
.
error
(
"--ask-vault-pass and --vault-password-file are mutually exclusive"
)
parser
.
error
(
"--ask-vault-pass and --vault-password-file are mutually exclusive"
)
inventory
=
ansible
.
inventory
.
Inventory
(
options
.
inventory
)
inventory
.
subset
(
options
.
subset
)
if
len
(
inventory
.
list_hosts
())
==
0
:
raise
errors
.
AnsibleError
(
"provided hosts list is empty"
)
sshpass
=
None
sshpass
=
None
sudopass
=
None
sudopass
=
None
su_pass
=
None
su_pass
=
None
...
@@ -160,12 +155,14 @@ def main(args):
...
@@ -160,12 +155,14 @@ def main(args):
if
not
(
os
.
path
.
isfile
(
playbook
)
or
stat
.
S_ISFIFO
(
os
.
stat
(
playbook
)
.
st_mode
)):
if
not
(
os
.
path
.
isfile
(
playbook
)
or
stat
.
S_ISFIFO
(
os
.
stat
(
playbook
)
.
st_mode
)):
raise
errors
.
AnsibleError
(
"the playbook:
%
s does not appear to be a file"
%
playbook
)
raise
errors
.
AnsibleError
(
"the playbook:
%
s does not appear to be a file"
%
playbook
)
inventory
=
ansible
.
inventory
.
Inventory
(
options
.
inventory
,
vault_password
=
vault_pass
)
inventory
.
subset
(
options
.
subset
)
if
len
(
inventory
.
list_hosts
())
==
0
:
raise
errors
.
AnsibleError
(
"provided hosts list is empty"
)
# run all playbooks specified on the command line
# run all playbooks specified on the command line
for
playbook
in
args
:
for
playbook
in
args
:
# let inventory know which playbooks are using so it can know the basedirs
inventory
.
set_playbook_basedir
(
os
.
path
.
dirname
(
playbook
))
stats
=
callbacks
.
AggregateStats
()
stats
=
callbacks
.
AggregateStats
()
playbook_cb
=
callbacks
.
PlaybookCallbacks
(
verbose
=
utils
.
VERBOSITY
)
playbook_cb
=
callbacks
.
PlaybookCallbacks
(
verbose
=
utils
.
VERBOSITY
)
if
options
.
step
:
if
options
.
step
:
...
...
lib/ansible/inventory/__init__.py
View file @
4e5eb755
This diff is collapsed.
Click to expand it.
lib/ansible/inventory/dir.py
View file @
4e5eb755
# (c) 2013, Daniel Hokka Zakrisson <daniel@hozac.com>
# (c) 2013, Daniel Hokka Zakrisson <daniel@hozac.com>
# (c) 2014, Serge van Ginderachter <serge@vanginderachter.be>
#
#
# This file is part of Ansible
# This file is part of Ansible
#
#
...
@@ -56,29 +57,168 @@ class InventoryDirectory(object):
...
@@ -56,29 +57,168 @@ class InventoryDirectory(object):
else
:
else
:
parser
=
InventoryParser
(
filename
=
fullpath
)
parser
=
InventoryParser
(
filename
=
fullpath
)
self
.
parsers
.
append
(
parser
)
self
.
parsers
.
append
(
parser
)
# This takes a lot of code because we can't directly use any of the objects, as they have to blend
for
name
,
group
in
parser
.
groups
.
iteritems
():
# retrieve all groups and hosts form the parser and add them to
if
name
not
in
self
.
groups
:
# self, don't look at group lists yet, to avoid
self
.
groups
[
name
]
=
group
# recursion trouble, but just make sure all objects exist in self
else
:
newgroups
=
parser
.
groups
.
values
()
# group is already there, copy variables
for
group
in
newgroups
:
# note: depth numbers on duplicates may be bogus
for
host
in
group
.
hosts
:
for
k
,
v
in
group
.
get_variables
()
.
iteritems
():
self
.
_add_host
(
host
)
self
.
groups
[
name
]
.
set_variable
(
k
,
v
)
for
group
in
newgroups
:
for
host
in
group
.
get_hosts
():
self
.
_add_group
(
group
)
if
host
.
name
not
in
self
.
hosts
:
self
.
hosts
[
host
.
name
]
=
host
# now check the objects lists so they contain only objects from
else
:
# self; membership data in groups is already fine (except all &
# host is already there, copy variables
# ungrouped, see later), but might still reference objects not in self
# note: depth numbers on duplicates may be bogus
for
group
in
self
.
groups
.
values
():
for
k
,
v
in
host
.
vars
.
iteritems
():
# iterate on a copy of the lists, as those lists get changed in
self
.
hosts
[
host
.
name
]
.
set_variable
(
k
,
v
)
# the loop
self
.
groups
[
name
]
.
add_host
(
self
.
hosts
[
host
.
name
])
# list with group's child group objects:
for
child
in
group
.
child_groups
[:]:
# This needs to be a second loop to ensure all the parent groups exist
if
child
!=
self
.
groups
[
child
.
name
]:
for
name
,
group
in
parser
.
groups
.
iteritems
():
group
.
child_groups
.
remove
(
child
)
for
ancestor
in
group
.
get_ancestors
():
group
.
child_groups
.
append
(
self
.
groups
[
child
.
name
])
self
.
groups
[
ancestor
.
name
]
.
add_child_group
(
self
.
groups
[
name
])
# list with group's parent group objects:
for
parent
in
group
.
parent_groups
[:]:
if
parent
!=
self
.
groups
[
parent
.
name
]:
group
.
parent_groups
.
remove
(
parent
)
group
.
parent_groups
.
append
(
self
.
groups
[
parent
.
name
])
# list with group's host objects:
for
host
in
group
.
hosts
[:]:
if
host
!=
self
.
hosts
[
host
.
name
]:
group
.
hosts
.
remove
(
host
)
group
.
hosts
.
append
(
self
.
hosts
[
host
.
name
])
# also check here that the group that contains host, is
# also contained in the host's group list
if
group
not
in
self
.
hosts
[
host
.
name
]
.
groups
:
self
.
hosts
[
host
.
name
]
.
groups
.
append
(
group
)
# extra checks on special groups all and ungrouped
# remove hosts from 'ungrouped' if they became member of other groups
if
'ungrouped'
in
self
.
groups
:
ungrouped
=
self
.
groups
[
'ungrouped'
]
# loop on a copy of ungrouped hosts, as we want to change that list
for
host
in
ungrouped
.
hosts
[:]:
if
len
(
host
.
groups
)
>
1
:
host
.
groups
.
remove
(
ungrouped
)
ungrouped
.
hosts
.
remove
(
host
)
# remove hosts from 'all' if they became member of other groups
# all should only contain direct children, not grandchildren
# direct children should have dept == 1
if
'all'
in
self
.
groups
:
allgroup
=
self
.
groups
[
'all'
]
# loop on a copy of all's child groups, as we want to change that list
for
group
in
allgroup
.
child_groups
[:]:
# groups might once have beeen added to all, and later be added
# to another group: we need to remove the link wit all then
if
len
(
group
.
parent_groups
)
>
1
:
# real children of all have just 1 parent, all
# this one has more, so not a direct child of all anymore
group
.
parent_groups
.
remove
(
allgroup
)
allgroup
.
child_groups
.
remove
(
group
)
elif
allgroup
not
in
group
.
parent_groups
:
# this group was once added to all, but doesn't list it as
# a parent any more; the info in the group is the correct
# info
allgroup
.
child_groups
.
remove
(
group
)
def
_add_group
(
self
,
group
):
""" Merge an existing group or add a new one;
Track parent and child groups, and hosts of the new one """
if
group
.
name
not
in
self
.
groups
:
# it's brand new, add him!
self
.
groups
[
group
.
name
]
=
group
if
self
.
groups
[
group
.
name
]
!=
group
:
# different object, merge
self
.
_merge_groups
(
self
.
groups
[
group
.
name
],
group
)
def
_add_host
(
self
,
host
):
if
host
.
name
not
in
self
.
hosts
:
# Papa's got a brand new host
self
.
hosts
[
host
.
name
]
=
host
if
self
.
hosts
[
host
.
name
]
!=
host
:
# different object, merge
self
.
_merge_hosts
(
self
.
hosts
[
host
.
name
],
host
)
def
_merge_groups
(
self
,
group
,
newgroup
):
""" Merge all of instance newgroup into group,
update parent/child relationships
group lists may still contain group objects that exist in self with
same name, but was instanciated as a different object in some other
inventory parser; these are handled later """
# name
if
group
.
name
!=
newgroup
.
name
:
raise
errors
.
AnsibleError
(
"Cannot merge group
%
s with
%
s"
%
(
group
.
name
,
newgroup
.
name
))
# depth
group
.
depth
=
max
([
group
.
depth
,
newgroup
.
depth
])
# hosts list (host objects are by now already added to self.hosts)
for
host
in
newgroup
.
hosts
:
grouphosts
=
dict
([(
h
.
name
,
h
)
for
h
in
group
.
hosts
])
if
host
.
name
in
grouphosts
:
# same host name but different object, merge
self
.
_merge_hosts
(
grouphosts
[
host
.
name
],
host
)
else
:
# new membership, add host to group from self
# group from self will also be added again to host.groups, but
# as different object
group
.
add_host
(
self
.
hosts
[
host
.
name
])
# now remove this the old object for group in host.groups
for
hostgroup
in
[
g
for
g
in
host
.
groups
]:
if
hostgroup
.
name
==
group
.
name
and
hostgroup
!=
self
.
groups
[
group
.
name
]:
self
.
hosts
[
host
.
name
]
.
groups
.
remove
(
hostgroup
)
# group child membership relation
for
newchild
in
newgroup
.
child_groups
:
# dict with existing child groups:
childgroups
=
dict
([(
g
.
name
,
g
)
for
g
in
group
.
child_groups
])
# check if child of new group is already known as a child
if
newchild
.
name
not
in
childgroups
:
self
.
groups
[
group
.
name
]
.
add_child_group
(
newchild
)
# group parent membership relation
for
newparent
in
newgroup
.
parent_groups
:
# dict with existing parent groups:
parentgroups
=
dict
([(
g
.
name
,
g
)
for
g
in
group
.
parent_groups
])
# check if parent of new group is already known as a parent
if
newparent
.
name
not
in
parentgroups
:
if
newparent
.
name
not
in
self
.
groups
:
# group does not exist yet in self, import him
self
.
groups
[
newparent
.
name
]
=
newparent
# group now exists but not yet as a parent here
self
.
groups
[
newparent
.
name
]
.
add_child_group
(
group
)
# variables
group
.
vars
=
utils
.
combine_vars
(
group
.
vars
,
newgroup
.
vars
)
def
_merge_hosts
(
self
,
host
,
newhost
):
""" Merge all of instance newhost into host """
# name
if
host
.
name
!=
newhost
.
name
:
raise
errors
.
AnsibleError
(
"Cannot merge host
%
s with
%
s"
%
(
host
.
name
,
newhost
.
name
))
# group membership relation
for
newgroup
in
newhost
.
groups
:
# dict with existing groups:
hostgroups
=
dict
([(
g
.
name
,
g
)
for
g
in
host
.
groups
])
# check if new group is already known as a group
if
newgroup
.
name
not
in
hostgroups
:
if
newgroup
.
name
not
in
self
.
groups
:
# group does not exist yet in self, import him
self
.
groups
[
newgroup
.
name
]
=
newgroup
# group now exists but doesn't have host yet
self
.
groups
[
newgroup
.
name
]
.
add_host
(
host
)
# variables
host
.
vars
=
utils
.
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 """
...
...
lib/ansible/inventory/group.py
View file @
4e5eb755
...
@@ -28,7 +28,8 @@ class Group(object):
...
@@ -28,7 +28,8 @@ class Group(object):
self
.
vars
=
{}
self
.
vars
=
{}
self
.
child_groups
=
[]
self
.
child_groups
=
[]
self
.
parent_groups
=
[]
self
.
parent_groups
=
[]
self
.
clear_hosts_cache
()
self
.
_hosts_cache
=
None
#self.clear_hosts_cache()
if
self
.
name
is
None
:
if
self
.
name
is
None
:
raise
Exception
(
"group name is required"
)
raise
Exception
(
"group name is required"
)
...
@@ -40,10 +41,26 @@ class Group(object):
...
@@ -40,10 +41,26 @@ class Group(object):
# don't add if it's already there
# don't add if it's already there
if
not
group
in
self
.
child_groups
:
if
not
group
in
self
.
child_groups
:
self
.
child_groups
.
append
(
group
)
self
.
child_groups
.
append
(
group
)
# update the depth of the child
group
.
depth
=
max
([
self
.
depth
+
1
,
group
.
depth
])
group
.
depth
=
max
([
self
.
depth
+
1
,
group
.
depth
])
group
.
parent_groups
.
append
(
self
)
# update the depth of the grandchildren
group
.
_check_children_depth
()
# now add self to child's parent_groups list, but only if there
# isn't already a group with the same name
if
not
self
.
name
in
[
g
.
name
for
g
in
group
.
parent_groups
]:
group
.
parent_groups
.
append
(
self
)
self
.
clear_hosts_cache
()
self
.
clear_hosts_cache
()
def
_check_children_depth
(
self
):
for
group
in
self
.
child_groups
:
group
.
depth
=
max
([
self
.
depth
+
1
,
group
.
depth
])
group
.
_check_children_depth
()
def
add_host
(
self
,
host
):
def
add_host
(
self
,
host
):
self
.
hosts
.
append
(
host
)
self
.
hosts
.
append
(
host
)
...
...
lib/ansible/inventory/ini.py
View file @
4e5eb755
...
@@ -45,6 +45,7 @@ class InventoryParser(object):
...
@@ -45,6 +45,7 @@ class InventoryParser(object):
self
.
_parse_base_groups
()
self
.
_parse_base_groups
()
self
.
_parse_group_children
()
self
.
_parse_group_children
()
self
.
_add_allgroup_children
()
self
.
_parse_group_variables
()
self
.
_parse_group_variables
()
return
self
.
groups
return
self
.
groups
...
@@ -69,6 +70,13 @@ class InventoryParser(object):
...
@@ -69,6 +70,13 @@ class InventoryParser(object):
# gamma sudo=True user=root
# gamma sudo=True user=root
# delta asdf=jkl favcolor=red
# delta asdf=jkl favcolor=red
def
_add_allgroup_children
(
self
):
for
group
in
self
.
groups
.
values
():
if
group
.
depth
==
0
and
group
.
name
!=
'all'
:
self
.
groups
[
'all'
]
.
add_child_group
(
group
)
def
_parse_base_groups
(
self
):
def
_parse_base_groups
(
self
):
# FIXME: refactor
# FIXME: refactor
...
@@ -87,11 +95,9 @@ class InventoryParser(object):
...
@@ -87,11 +95,9 @@ class InventoryParser(object):
active_group_name
=
active_group_name
.
rsplit
(
":"
,
1
)[
0
]
active_group_name
=
active_group_name
.
rsplit
(
":"
,
1
)[
0
]
if
active_group_name
not
in
self
.
groups
:
if
active_group_name
not
in
self
.
groups
:
new_group
=
self
.
groups
[
active_group_name
]
=
Group
(
name
=
active_group_name
)
new_group
=
self
.
groups
[
active_group_name
]
=
Group
(
name
=
active_group_name
)
all
.
add_child_group
(
new_group
)
active_group_name
=
None
active_group_name
=
None
elif
active_group_name
not
in
self
.
groups
:
elif
active_group_name
not
in
self
.
groups
:
new_group
=
self
.
groups
[
active_group_name
]
=
Group
(
name
=
active_group_name
)
new_group
=
self
.
groups
[
active_group_name
]
=
Group
(
name
=
active_group_name
)
all
.
add_child_group
(
new_group
)
elif
line
.
startswith
(
";"
)
or
line
==
''
:
elif
line
.
startswith
(
";"
)
or
line
==
''
:
pass
pass
elif
active_group_name
:
elif
active_group_name
:
...
...
lib/ansible/inventory/script.py
View file @
4e5eb755
...
@@ -46,6 +46,7 @@ class InventoryScript(object):
...
@@ -46,6 +46,7 @@ class InventoryScript(object):
self
.
host_vars_from_top
=
None
self
.
host_vars_from_top
=
None
self
.
groups
=
self
.
_parse
(
stderr
)
self
.
groups
=
self
.
_parse
(
stderr
)
def
_parse
(
self
,
err
):
def
_parse
(
self
,
err
):
all_hosts
=
{}
all_hosts
=
{}
...
@@ -63,7 +64,7 @@ class InventoryScript(object):
...
@@ -63,7 +64,7 @@ class InventoryScript(object):
raise
errors
.
AnsibleError
(
"failed to parse executable inventory script results:
%
s"
%
self
.
raw
)
raise
errors
.
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
():
# in Ansible 1.3 and later, a "_meta" subelement may contain
# in Ansible 1.3 and later, a "_meta" subelement may contain
# a variable "hostvars" which contains a hash for each host
# a variable "hostvars" which contains a hash for each host
# if this "hostvars" exists at all then do not call --host for each
# if this "hostvars" exists at all then do not call --host for each
...
@@ -100,8 +101,6 @@ class InventoryScript(object):
...
@@ -100,8 +101,6 @@ class InventoryScript(object):
all
.
set_variable
(
k
,
v
)
all
.
set_variable
(
k
,
v
)
else
:
else
:
group
.
set_variable
(
k
,
v
)
group
.
set_variable
(
k
,
v
)
if
group
.
name
!=
all
.
name
:
all
.
add_child_group
(
group
)
# Separate loop to ensure all groups are defined
# Separate loop to ensure all groups are defined
for
(
group_name
,
data
)
in
self
.
raw
.
items
():
for
(
group_name
,
data
)
in
self
.
raw
.
items
():
...
@@ -111,6 +110,11 @@ class InventoryScript(object):
...
@@ -111,6 +110,11 @@ class InventoryScript(object):
for
child_name
in
data
[
'children'
]:
for
child_name
in
data
[
'children'
]:
if
child_name
in
groups
:
if
child_name
in
groups
:
groups
[
group_name
]
.
add_child_group
(
groups
[
child_name
])
groups
[
group_name
]
.
add_child_group
(
groups
[
child_name
])
for
group
in
groups
.
values
():
if
group
.
depth
==
0
and
group
.
name
!=
'all'
:
all
.
add_child_group
(
group
)
return
groups
return
groups
def
get_host_variables
(
self
,
host
):
def
get_host_variables
(
self
,
host
):
...
...
lib/ansible/inventory/vars_plugins/group_vars.py
deleted
100644 → 0
View file @
6d24f437
# (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
import
errno
from
ansible
import
errors
from
ansible
import
utils
import
ansible.constants
as
C
def
_load_vars
(
basepath
,
results
,
vault_password
=
None
):
"""
Load variables from any potential yaml filename combinations of basepath,
returning result.
"""
paths_to_check
=
[
""
.
join
([
basepath
,
ext
])
for
ext
in
C
.
YAML_FILENAME_EXTENSIONS
]
found_paths
=
[]
for
path
in
paths_to_check
:
found
,
results
=
_load_vars_from_path
(
path
,
results
,
vault_password
=
vault_password
)
if
found
:
found_paths
.
append
(
path
)
# disallow the potentially confusing situation that there are multiple
# variable files for the same name. For example if both group_vars/all.yml
# and group_vars/all.yaml
if
len
(
found_paths
)
>
1
:
raise
errors
.
AnsibleError
(
"Multiple variable files found. "
"There should only be one.
%
s"
%
(
found_paths
,
))
return
results
def
_load_vars_from_path
(
path
,
results
,
vault_password
=
None
):
"""
Robustly access the file at path and load variables, carefully reporting
errors in a friendly/informative way.
Return the tuple (found, new_results, )
"""
try
:
# in the case of a symbolic link, we want the stat of the link itself,
# not its target
pathstat
=
os
.
lstat
(
path
)
except
os
.
error
,
err
:
# most common case is that nothing exists at that path.
if
err
.
errno
==
errno
.
ENOENT
:
return
False
,
results
# otherwise this is a condition we should report to the user
raise
errors
.
AnsibleError
(
"
%
s is not accessible:
%
s."
" Please check its permissions."
%
(
path
,
err
.
strerror
))
# symbolic link
if
stat
.
S_ISLNK
(
pathstat
.
st_mode
):
try
:
target
=
os
.
path
.
realpath
(
path
)
except
os
.
error
,
err2
:
raise
errors
.
AnsibleError
(
"The symbolic link at
%
s "
"is not readable:
%
s. Please check its permissions."
%
(
path
,
err2
.
strerror
,
))
# follow symbolic link chains by recursing, so we repeat the same
# permissions checks above and provide useful errors.
return
_load_vars_from_path
(
target
,
results
)
# directory
if
stat
.
S_ISDIR
(
pathstat
.
st_mode
):
# support organizing variables across multiple files in a directory
return
True
,
_load_vars_from_folder
(
path
,
results
,
vault_password
=
vault_password
)
# regular file
elif
stat
.
S_ISREG
(
pathstat
.
st_mode
):
data
=
utils
.
parse_yaml_from_file
(
path
,
vault_password
=
vault_password
)
if
data
and
type
(
data
)
!=
dict
:
raise
errors
.
AnsibleError
(
"
%
s must be stored as a dictionary/hash"
%
path
)
elif
data
is
None
:
data
=
{}
# combine vars overrides by default but can be configured to do a
# hash merge in settings
results
=
utils
.
combine_vars
(
results
,
data
)
return
True
,
results
# something else? could be a fifo, socket, device, etc.
else
:
raise
errors
.
AnsibleError
(
"Expected a variable file or directory "
"but found a non-file object at path
%
s"
%
(
path
,
))
def
_load_vars_from_folder
(
folder_path
,
results
,
vault_password
=
None
):
"""
Load all variables within a folder recursively.
"""
# this function and _load_vars_from_path are mutually recursive
try
:
names
=
os
.
listdir
(
folder_path
)
except
os
.
error
,
err
:
raise
errors
.
AnsibleError
(
"This folder cannot be listed:
%
s:
%
s."
%
(
folder_path
,
err
.
strerror
))
# evaluate files in a stable order rather than whatever order the
# filesystem lists them.
names
.
sort
()
# do not parse hidden files or dirs, e.g. .svn/
paths
=
[
os
.
path
.
join
(
folder_path
,
name
)
for
name
in
names
if
not
name
.
startswith
(
'.'
)]
for
path
in
paths
:
_found
,
results
=
_load_vars_from_path
(
path
,
results
,
vault_password
=
vault_password
)
return
results
class
VarsModule
(
object
):
"""
Loads variables from group_vars/<groupname> and host_vars/<hostname> in directories parallel
to the inventory base directory or in the same directory as the playbook. Variables in the playbook
dir will win over the inventory dir if files are in both.
"""
def
__init__
(
self
,
inventory
):
""" constructor """
self
.
inventory
=
inventory
def
run
(
self
,
host
,
vault_password
=
None
):
""" main body of the plugin, does actual loading """
inventory
=
self
.
inventory
basedir
=
inventory
.
playbook_basedir
()
if
basedir
is
not
None
:
basedir
=
os
.
path
.
abspath
(
basedir
)
self
.
pb_basedir
=
basedir
# sort groups by depth so deepest groups can override the less deep ones
groupz
=
sorted
(
inventory
.
groups_for_host
(
host
.
name
),
key
=
lambda
g
:
g
.
depth
)
groups
=
[
g
.
name
for
g
in
groupz
]
inventory_basedir
=
inventory
.
basedir
()
results
=
{}
scan_pass
=
0
# look in both the inventory base directory and the playbook base directory
for
basedir
in
[
inventory_basedir
,
self
.
pb_basedir
]:
# this can happen from particular API usages, particularly if not run
# from /usr/bin/ansible-playbook
if
basedir
is
None
:
continue
scan_pass
=
scan_pass
+
1
# it's not an eror if the directory does not exist, keep moving
if
not
os
.
path
.
exists
(
basedir
):
continue
# save work of second scan if the directories are the same
if
inventory_basedir
==
self
.
pb_basedir
and
scan_pass
!=
1
:
continue
# load vars in dir/group_vars/name_of_group
for
group
in
groups
:
base_path
=
os
.
path
.
join
(
basedir
,
"group_vars/
%
s"
%
group
)
results
=
_load_vars
(
base_path
,
results
,
vault_password
=
vault_password
)
# same for hostvars in dir/host_vars/name_of_host
base_path
=
os
.
path
.
join
(
basedir
,
"host_vars/
%
s"
%
host
.
name
)
results
=
_load_vars
(
base_path
,
results
,
vault_password
=
vault_password
)
# all done, results is a dictionary of variables for this particular host.
return
results
lib/ansible/inventory/vars_plugins/noop.py
0 → 100644
View file @
4e5eb755
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
# (c) 2014, Serge van Ginderachter <serge@vanginderachter.be>
#
# 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/>.
class
VarsModule
(
object
):
"""
Loads variables for groups and/or hosts
"""
def
__init__
(
self
,
inventory
):
""" constructor """
self
.
inventory
=
inventory
self
.
inventory_basedir
=
inventory
.
basedir
()
def
run
(
self
,
host
,
vault_password
=
None
):
""" For backwards compatibility, when only vars per host were retrieved
This method should return both host specific vars as well as vars
calculated from groups it is a member of """
return
{}
def
get_host_vars
(
self
,
host
,
vault_password
=
None
):
""" Get host specific variables. """
return
{}
def
get_group_vars
(
self
,
group
,
vault_password
=
None
):
""" Get group specific variables. """
return
{}
lib/ansible/playbook/__init__.py
View file @
4e5eb755
...
@@ -164,6 +164,10 @@ class PlayBook(object):
...
@@ -164,6 +164,10 @@ class PlayBook(object):
self
.
basedir
=
os
.
path
.
dirname
(
playbook
)
or
'.'
self
.
basedir
=
os
.
path
.
dirname
(
playbook
)
or
'.'
utils
.
plugins
.
push_basedir
(
self
.
basedir
)
utils
.
plugins
.
push_basedir
(
self
.
basedir
)
# let inventory know the playbook basedir so it can load more vars
self
.
inventory
.
set_playbook_basedir
(
self
.
basedir
)
vars
=
extra_vars
.
copy
()
vars
=
extra_vars
.
copy
()
vars
[
'playbook_dir'
]
=
self
.
basedir
vars
[
'playbook_dir'
]
=
self
.
basedir
if
self
.
inventory
.
basedir
()
is
not
None
:
if
self
.
inventory
.
basedir
()
is
not
None
:
...
...
lib/ansible/utils/__init__.py
View file @
4e5eb755
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
# (c) 2012
-2014
, Michael DeHaan <michael.dehaan@gmail.com>
#
#
# This file is part of Ansible
# This file is part of Ansible
#
#
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
# You should have received a copy of the GNU General Public License
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import
errno
import
sys
import
sys
import
re
import
re
import
os
import
os
...
@@ -620,18 +621,19 @@ def merge_hash(a, b):
...
@@ -620,18 +621,19 @@ def merge_hash(a, b):
''' recursively merges hash b into a
''' recursively merges hash b into a
keys from b take precedence over keys from a '''
keys from b take precedence over keys from a '''
result
=
copy
.
deepcopy
(
a
)
result
=
{}
# next, iterate over b keys and values
for
dicts
in
a
,
b
:
for
k
,
v
in
b
.
iteritems
():
# next, iterate over b keys and values
# if there's already such key in a
for
k
,
v
in
dicts
.
iteritems
():
# and that key contains dict
# if there's already such key in a
if
k
in
result
and
isinstance
(
result
[
k
],
dict
):
# and that key contains dict
# merge those dicts recursively
if
k
in
result
and
isinstance
(
result
[
k
],
dict
):
result
[
k
]
=
merge_hash
(
a
[
k
],
v
)
# merge those dicts recursively
else
:
result
[
k
]
=
merge_hash
(
a
[
k
],
v
)
# otherwise, just copy a value from b to a
else
:
result
[
k
]
=
v
# otherwise, just copy a value from b to a
result
[
k
]
=
v
return
result
return
result
...
@@ -1208,5 +1210,112 @@ def before_comment(msg):
...
@@ -1208,5 +1210,112 @@ def before_comment(msg):
msg
=
msg
.
replace
(
"**NOT_A_COMMENT**"
,
"#"
)
msg
=
msg
.
replace
(
"**NOT_A_COMMENT**"
,
"#"
)
return
msg
return
msg
def
load_vars
(
basepath
,
results
,
vault_password
=
None
):
"""
Load variables from any potential yaml filename combinations of basepath,
returning result.
"""
paths_to_check
=
[
""
.
join
([
basepath
,
ext
])
for
ext
in
C
.
YAML_FILENAME_EXTENSIONS
]
found_paths
=
[]
for
path
in
paths_to_check
:
found
,
results
=
_load_vars_from_path
(
path
,
results
,
vault_password
=
vault_password
)
if
found
:
found_paths
.
append
(
path
)
# disallow the potentially confusing situation that there are multiple
# variable files for the same name. For example if both group_vars/all.yml
# and group_vars/all.yaml
if
len
(
found_paths
)
>
1
:
raise
errors
.
AnsibleError
(
"Multiple variable files found. "
"There should only be one.
%
s"
%
(
found_paths
,
))
return
results
## load variables from yaml files/dirs
# e.g. host/group_vars
#
def
_load_vars_from_path
(
path
,
results
,
vault_password
=
None
):
"""
Robustly access the file at path and load variables, carefully reporting
errors in a friendly/informative way.
Return the tuple (found, new_results, )
"""
try
:
# in the case of a symbolic link, we want the stat of the link itself,
# not its target
pathstat
=
os
.
lstat
(
path
)
except
os
.
error
,
err
:
# most common case is that nothing exists at that path.
if
err
.
errno
==
errno
.
ENOENT
:
return
False
,
results
# otherwise this is a condition we should report to the user
raise
errors
.
AnsibleError
(
"
%
s is not accessible:
%
s."
" Please check its permissions."
%
(
path
,
err
.
strerror
))
# symbolic link
if
stat
.
S_ISLNK
(
pathstat
.
st_mode
):
try
:
target
=
os
.
path
.
realpath
(
path
)
except
os
.
error
,
err2
:
raise
errors
.
AnsibleError
(
"The symbolic link at
%
s "
"is not readable:
%
s. Please check its permissions."
%
(
path
,
err2
.
strerror
,
))
# follow symbolic link chains by recursing, so we repeat the same
# permissions checks above and provide useful errors.
return
_load_vars_from_path
(
target
,
results
)
# directory
if
stat
.
S_ISDIR
(
pathstat
.
st_mode
):
# support organizing variables across multiple files in a directory
return
True
,
_load_vars_from_folder
(
path
,
results
,
vault_password
=
vault_password
)
# regular file
elif
stat
.
S_ISREG
(
pathstat
.
st_mode
):
data
=
parse_yaml_from_file
(
path
,
vault_password
=
vault_password
)
if
type
(
data
)
!=
dict
:
raise
errors
.
AnsibleError
(
"
%
s must be stored as a dictionary/hash"
%
path
)
# combine vars overrides by default but can be configured to do a
# hash merge in settings
results
=
combine_vars
(
results
,
data
)
return
True
,
results
# something else? could be a fifo, socket, device, etc.
else
:
raise
errors
.
AnsibleError
(
"Expected a variable file or directory "
"but found a non-file object at path
%
s"
%
(
path
,
))
def
_load_vars_from_folder
(
folder_path
,
results
,
vault_password
=
None
):
"""
Load all variables within a folder recursively.
"""
# this function and _load_vars_from_path are mutually recursive
try
:
names
=
os
.
listdir
(
folder_path
)
except
os
.
error
,
err
:
raise
errors
.
AnsibleError
(
"This folder cannot be listed:
%
s:
%
s."
%
(
folder_path
,
err
.
strerror
))
# evaluate files in a stable order rather than whatever order the
# filesystem lists them.
names
.
sort
()
# do not parse hidden files or dirs, e.g. .svn/
paths
=
[
os
.
path
.
join
(
folder_path
,
name
)
for
name
in
names
if
not
name
.
startswith
(
'.'
)]
for
path
in
paths
:
_found
,
results
=
_load_vars_from_path
(
path
,
results
,
vault_password
=
vault_password
)
return
results
test/units/TestInventory.py
View file @
4e5eb755
...
@@ -433,7 +433,7 @@ class TestInventory(unittest.TestCase):
...
@@ -433,7 +433,7 @@ class TestInventory(unittest.TestCase):
expected_vars
=
{
'inventory_hostname'
:
'zeus'
,
expected_vars
=
{
'inventory_hostname'
:
'zeus'
,
'inventory_hostname_short'
:
'zeus'
,
'inventory_hostname_short'
:
'zeus'
,
'group_names'
:
[
'greek'
,
'major-god'
,
'ungrouped'
],
'group_names'
:
[
'greek'
,
'major-god'
],
'var_a'
:
'3#4'
}
'var_a'
:
'3#4'
}
print
"HOST VARS=
%
s"
%
host_vars
print
"HOST VARS=
%
s"
%
host_vars
...
@@ -451,3 +451,55 @@ class TestInventory(unittest.TestCase):
...
@@ -451,3 +451,55 @@ class TestInventory(unittest.TestCase):
def
test_dir_inventory_skip_extension
(
self
):
def
test_dir_inventory_skip_extension
(
self
):
inventory
=
self
.
dir_inventory
()
inventory
=
self
.
dir_inventory
()
assert
'skipme'
not
in
[
h
.
name
for
h
in
inventory
.
get_hosts
()]
assert
'skipme'
not
in
[
h
.
name
for
h
in
inventory
.
get_hosts
()]
def
test_dir_inventory_group_hosts
(
self
):
inventory
=
self
.
dir_inventory
()
expected_groups
=
{
'all'
:
[
'morpheus'
,
'thor'
,
'zeus'
],
'major-god'
:
[
'thor'
,
'zeus'
],
'minor-god'
:
[
'morpheus'
],
'norse'
:
[
'thor'
],
'greek'
:
[
'morpheus'
,
'zeus'
],
'ungrouped'
:
[]}
actual_groups
=
{}
for
group
in
inventory
.
get_groups
():
actual_groups
[
group
.
name
]
=
sorted
([
h
.
name
for
h
in
group
.
get_hosts
()])
print
"INVENTORY groups[
%
s].hosts=
%
s"
%
(
group
.
name
,
actual_groups
[
group
.
name
])
print
"EXPECTED groups[
%
s].hosts=
%
s"
%
(
group
.
name
,
expected_groups
[
group
.
name
])
assert
actual_groups
==
expected_groups
def
test_dir_inventory_groups_for_host
(
self
):
inventory
=
self
.
dir_inventory
()
expected_groups_for_host
=
{
'morpheus'
:
[
'all'
,
'greek'
,
'minor-god'
],
'thor'
:
[
'all'
,
'major-god'
,
'norse'
],
'zeus'
:
[
'all'
,
'greek'
,
'major-god'
]}
actual_groups_for_host
=
{}
for
(
host
,
expected
)
in
expected_groups_for_host
.
iteritems
():
groups
=
inventory
.
groups_for_host
(
host
)
names
=
sorted
([
g
.
name
for
g
in
groups
])
actual_groups_for_host
[
host
]
=
names
print
"INVENTORY groups_for_host(
%
s)=
%
s"
%
(
host
,
names
)
print
"EXPECTED groups_for_host(
%
s)=
%
s"
%
(
host
,
expected
)
assert
actual_groups_for_host
==
expected_groups_for_host
def
test_dir_inventory_groups_list
(
self
):
inventory
=
self
.
dir_inventory
()
inventory_groups
=
inventory
.
groups_list
()
expected_groups
=
{
'all'
:
[
'morpheus'
,
'thor'
,
'zeus'
],
'major-god'
:
[
'thor'
,
'zeus'
],
'minor-god'
:
[
'morpheus'
],
'norse'
:
[
'thor'
],
'greek'
:
[
'morpheus'
,
'zeus'
],
'ungrouped'
:
[]}
for
(
name
,
expected_hosts
)
in
expected_groups
.
iteritems
():
inventory_groups
[
name
]
=
sorted
(
inventory_groups
.
get
(
name
,
[]))
print
"INVENTORY groups_list['
%
s']=
%
s"
%
(
name
,
inventory_groups
[
name
])
print
"EXPECTED groups_list['
%
s']=
%
s"
%
(
name
,
expected_hosts
)
assert
inventory_groups
==
expected_groups
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment