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
4b0d52d2
Commit
4b0d52d2
authored
Sep 22, 2015
by
Toshio Kuratomi
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #12420 from ansible/win_prefix_modules
Fix for user defined modules not overriding modules from core.
parents
1076155d
18e2ee16
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
131 additions
and
64 deletions
+131
-64
lib/ansible/plugins/__init__.py
+100
-44
lib/ansible/plugins/action/__init__.py
+25
-19
lib/ansible/plugins/connection/__init__.py
+4
-0
lib/ansible/plugins/connection/winrm.py
+2
-1
No files found.
lib/ansible/plugins/__init__.py
View file @
4b0d52d2
...
@@ -26,10 +26,9 @@ import inspect
...
@@ -26,10 +26,9 @@ import inspect
import
os
import
os
import
os.path
import
os.path
import
sys
import
sys
from
collections
import
defaultdict
from
ansible
import
constants
as
C
from
ansible
import
constants
as
C
from
ansible.utils.unicode
import
to_unicode
from
ansible
import
errors
try
:
try
:
from
__main__
import
display
from
__main__
import
display
...
@@ -37,6 +36,7 @@ except ImportError:
...
@@ -37,6 +36,7 @@ except ImportError:
from
ansible.utils.display
import
Display
from
ansible.utils.display
import
Display
display
=
Display
()
display
=
Display
()
# Global so that all instances of a PluginLoader will share the caches
MODULE_CACHE
=
{}
MODULE_CACHE
=
{}
PATH_CACHE
=
{}
PATH_CACHE
=
{}
PLUGIN_PATH_CACHE
=
{}
PLUGIN_PATH_CACHE
=
{}
...
@@ -68,7 +68,7 @@ class PluginLoader:
...
@@ -68,7 +68,7 @@ class PluginLoader:
if
not
class_name
in
PATH_CACHE
:
if
not
class_name
in
PATH_CACHE
:
PATH_CACHE
[
class_name
]
=
None
PATH_CACHE
[
class_name
]
=
None
if
not
class_name
in
PLUGIN_PATH_CACHE
:
if
not
class_name
in
PLUGIN_PATH_CACHE
:
PLUGIN_PATH_CACHE
[
class_name
]
=
{}
PLUGIN_PATH_CACHE
[
class_name
]
=
defaultdict
(
dict
)
self
.
_module_cache
=
MODULE_CACHE
[
class_name
]
self
.
_module_cache
=
MODULE_CACHE
[
class_name
]
self
.
_paths
=
PATH_CACHE
[
class_name
]
self
.
_paths
=
PATH_CACHE
[
class_name
]
...
@@ -169,9 +169,30 @@ class PluginLoader:
...
@@ -169,9 +169,30 @@ class PluginLoader:
# look for any plugins installed in the package subtree
# look for any plugins installed in the package subtree
ret
.
extend
(
self
.
_get_package_paths
())
ret
.
extend
(
self
.
_get_package_paths
())
# HACK: because powershell modules are in the same directory
# hierarchy as other modules we have to process them last. This is
# because powershell only works on windows but the other modules work
# anywhere (possibly including windows if the correct language
# interpreter is installed). the non-powershell modules can have any
# file extension and thus powershell modules are picked up in that.
# The non-hack way to fix this is to have powershell modules be
# a different PluginLoader/ModuleLoader. But that requires changing
# other things too (known thing to change would be PATHS_CACHE,
# PLUGIN_PATHS_CACHE, and MODULE_CACHE. Since those three dicts key
# on the class_name and neither regular modules nor powershell modules
# would have class_names, they would not work as written.
reordered_paths
=
[]
win_dirs
=
[]
for
path
in
ret
:
if
path
.
endswith
(
'windows'
):
win_dirs
.
append
(
path
)
else
:
reordered_paths
.
append
(
path
)
reordered_paths
.
extend
(
win_dirs
)
# cache and return the result
# cache and return the result
self
.
_paths
=
re
t
self
.
_paths
=
re
ordered_paths
return
re
t
return
re
ordered_paths
def
add_directory
(
self
,
directory
,
with_subdir
=
False
):
def
add_directory
(
self
,
directory
,
with_subdir
=
False
):
...
@@ -187,55 +208,90 @@ class PluginLoader:
...
@@ -187,55 +208,90 @@ class PluginLoader:
self
.
_extra_dirs
.
append
(
directory
)
self
.
_extra_dirs
.
append
(
directory
)
self
.
_paths
=
None
self
.
_paths
=
None
def
find_plugin
(
self
,
name
,
suffixes
=
None
):
def
find_plugin
(
self
,
name
,
mod_type
=
''
):
''' Find a plugin named name '''
''' Find a plugin named name '''
if
not
suffixes
:
# The particular cache to look for modules within. This matches the
if
self
.
class_name
:
# requested mod_type
suffixes
=
[
'.py'
]
pull_cache
=
self
.
_plugin_path_cache
[
mod_type
]
else
:
try
:
suffixes
=
[
'.py'
,
''
]
return
pull_cache
[
name
]
except
KeyError
:
potential_names
=
frozenset
(
'
%
s
%
s'
%
(
name
,
s
)
for
s
in
suffixes
)
# Cache miss. Now let's find the plugin
for
full_name
in
potential_names
:
pass
if
full_name
in
self
.
_plugin_path_cache
:
return
self
.
_plugin_path_cache
[
full_name
]
if
mod_type
:
suffix
=
mod_type
elif
self
.
class_name
:
# Ansible plugins that run in the controller process (most plugins)
suffix
=
'.py'
else
:
# Only Ansible Modules. Ansible modules can be any executable so
# they can have any suffix
suffix
=
''
### FIXME:
# Instead of using the self._paths cache (PATH_CACHE) and
# self._searched_paths we could use an iterator. Before enabling that
# we need to make sure we don't want to add additional directories
# (add_directory()) once we start using the iterator. Currently, it
# looks like _get_paths() never forces a cache refresh so if we expect
# additional directories to be added later, it is buggy.
for
path
in
(
p
for
p
in
self
.
_get_paths
()
if
p
not
in
self
.
_searched_paths
and
os
.
path
.
isdir
(
p
)):
try
:
full_paths
=
(
os
.
path
.
join
(
path
,
f
)
for
f
in
os
.
listdir
(
path
))
except
OSError
as
e
:
display
.
warning
(
"Error accessing plugin paths:
%
s"
%
str
(
e
))
for
full_path
in
(
f
for
f
in
full_paths
if
os
.
path
.
isfile
(
f
)
and
not
f
.
endswith
(
'__init__.py'
)):
full_name
=
os
.
path
.
basename
(
full_path
)
# HACK: We have no way of executing python byte
# compiled files as ansible modules so specifically exclude them
if
full_path
.
endswith
((
'.pyc'
,
'.pyo'
)):
continue
found
=
None
splitname
=
os
.
path
.
splitext
(
full_name
)
for
path
in
[
p
for
p
in
self
.
_get_paths
()
if
p
not
in
self
.
_searched_paths
]:
base_name
=
splitname
[
0
]
if
os
.
path
.
isdir
(
path
):
try
:
try
:
full_paths
=
(
os
.
path
.
join
(
path
,
f
)
for
f
in
os
.
listdir
(
path
))
extension
=
splitname
[
1
]
except
OSError
as
e
:
except
IndexError
:
display
.
warning
(
"Error accessing plugin paths:
%
s"
%
str
(
e
))
extension
=
''
for
full_path
in
(
f
for
f
in
full_paths
if
os
.
path
.
isfile
(
f
)):
for
suffix
in
suffixes
:
# Module found, now enter it into the caches that match
if
full_path
.
endswith
(
suffix
):
# this file
full_name
=
os
.
path
.
basename
(
full_path
)
if
base_name
not
in
self
.
_plugin_path_cache
[
''
]:
break
self
.
_plugin_path_cache
[
''
][
base_name
]
=
full_path
else
:
# Yes, this is a for-else: http://bit.ly/1ElPkyg
continue
if
full_name
not
in
self
.
_plugin_path_cache
[
''
]:
self
.
_plugin_path_cache
[
''
][
full_name
]
=
full_path
if
base_name
not
in
self
.
_plugin_path_cache
[
extension
]:
self
.
_plugin_path_cache
[
extension
][
base_name
]
=
full_path
if
full_name
not
in
self
.
_plugin_path_cache
:
if
full_name
not
in
self
.
_plugin_path_cache
[
extension
]
:
self
.
_plugin_path_cache
[
full_name
]
=
full_path
self
.
_plugin_path_cache
[
extension
]
[
full_name
]
=
full_path
self
.
_searched_paths
.
add
(
path
)
self
.
_searched_paths
.
add
(
path
)
for
full_name
in
potential_names
:
try
:
if
full_name
in
self
.
_plugin_path_cache
:
return
pull_cache
[
name
]
return
self
.
_plugin_path_cache
[
full_name
]
except
KeyError
:
# Didn't find the plugin in this directory. Load modules from
# the next one
pass
# if nothing is found, try finding alias/deprecated
# if nothing is found, try finding alias/deprecated
if
not
name
.
startswith
(
'_'
):
if
not
name
.
startswith
(
'_'
):
for
alias_name
in
(
'_
%
s'
%
n
for
n
in
potential_names
):
alias_name
=
'_'
+
name
# We've already cached all the paths at this point
# We've already cached all the paths at this point
if
alias_name
in
self
.
_plugin_path
_cache
:
if
alias_name
in
pull
_cache
:
if
not
os
.
path
.
islink
(
self
.
_plugin_path
_cache
[
alias_name
]):
if
not
os
.
path
.
islink
(
pull
_cache
[
alias_name
]):
display
.
deprecated
(
'
%
s is kept for backwards compatibility '
display
.
deprecated
(
'
%
s is kept for backwards compatibility '
'but usage is discouraged. The module '
'but usage is discouraged. The module '
'documentation details page may explain '
'documentation details page may explain '
'more about this rationale.'
%
'more about this rationale.'
%
name
.
lstrip
(
'_'
))
name
.
lstrip
(
'_'
))
return
self
.
_plugin_path
_cache
[
alias_name
]
return
pull
_cache
[
alias_name
]
return
None
return
None
...
...
lib/ansible/plugins/action/__init__.py
View file @
4b0d52d2
...
@@ -69,30 +69,36 @@ class ActionBase:
...
@@ -69,30 +69,36 @@ class ActionBase:
'''
'''
# Search module path(s) for named module.
# Search module path(s) for named module.
module_suffixes
=
getattr
(
self
.
_connection
,
'default_suffixes'
,
None
)
for
mod_type
in
self
.
_connection
.
module_implementation_preferences
:
# Check to determine if PowerShell modules are supported, and apply
# Check to determine if PowerShell modules are supported, and apply
# some fixes (hacks) to module name + args.
# some fixes (hacks) to module name + args.
if
mod_type
==
'.ps1'
:
if
module_suffixes
and
'.ps1'
in
module_suffixes
:
# win_stat, win_file, and win_copy are not just like their
# Use Windows versions of stat/file/copy modules when called from
# python counterparts but they are compatible enough for our
# within other action plugins.
# internal usage
if
module_name
in
(
'stat'
,
'file'
,
'copy'
)
and
self
.
_task
.
action
!=
module_name
:
if
module_name
in
(
'stat'
,
'file'
,
'copy'
)
and
self
.
_task
.
action
!=
module_name
:
module_name
=
'win_
%
s'
%
module_name
module_name
=
'win_
%
s'
%
module_name
# Remove extra quotes surrounding path parameters before sending to module.
if
module_name
in
(
'win_stat'
,
'win_file'
,
'win_copy'
,
'slurp'
)
and
module_args
and
hasattr
(
self
.
_connection
.
_shell
,
'_unquote'
):
# Remove extra quotes surrounding path parameters before sending to module.
for
key
in
(
'src'
,
'dest'
,
'path'
):
if
module_name
in
(
'win_stat'
,
'win_file'
,
'win_copy'
,
'slurp'
)
and
module_args
and
hasattr
(
self
.
_connection
.
_shell
,
'_unquote'
):
if
key
in
module_args
:
for
key
in
(
'src'
,
'dest'
,
'path'
):
module_args
[
key
]
=
self
.
_connection
.
_shell
.
_unquote
(
module_args
[
key
])
if
key
in
module_args
:
module_args
[
key
]
=
self
.
_connection
.
_shell
.
_unquote
(
module_args
[
key
])
module_path
=
self
.
_shared_loader_obj
.
module_loader
.
find_plugin
(
module_name
,
module_suffixes
)
if
module_path
is
None
:
module_path
=
self
.
_shared_loader_obj
.
module_loader
.
find_plugin
(
module_name
,
mod_type
)
if
module_path
:
break
else
:
# This is a for-else: http://bit.ly/1ElPkyg
# FIXME: Why is it necessary to look for the windows version?
# Shouldn't all modules be installed?
#
# Use Windows version of ping module to check module paths when
# Use Windows version of ping module to check module paths when
# using a connection that supports .ps1 suffixes.
# using a connection that supports .ps1 suffixes.
if
module_suffixes
and
'.ps1'
in
module_suffix
es
:
if
'.ps1'
in
self
.
_connection
.
module_implementation_preferenc
es
:
ping_module
=
'win_ping'
ping_module
=
'win_ping'
else
:
else
:
ping_module
=
'ping'
ping_module
=
'ping'
module_path2
=
self
.
_shared_loader_obj
.
module_loader
.
find_plugin
(
ping_module
,
module_suffix
es
)
module_path2
=
self
.
_shared_loader_obj
.
module_loader
.
find_plugin
(
ping_module
,
self
.
_connection
.
module_implementation_preferenc
es
)
if
module_path2
is
not
None
:
if
module_path2
is
not
None
:
raise
AnsibleError
(
"The module
%
s was not found in configured module paths"
%
(
module_name
))
raise
AnsibleError
(
"The module
%
s was not found in configured module paths"
%
(
module_name
))
else
:
else
:
...
...
lib/ansible/plugins/connection/__init__.py
View file @
4b0d52d2
...
@@ -57,6 +57,10 @@ class ConnectionBase(with_metaclass(ABCMeta, object)):
...
@@ -57,6 +57,10 @@ class ConnectionBase(with_metaclass(ABCMeta, object)):
has_pipelining
=
False
has_pipelining
=
False
become_methods
=
C
.
BECOME_METHODS
become_methods
=
C
.
BECOME_METHODS
# When running over this connection type, prefer modules written in a certain language
# as discovered by the specified file extension. An empty string as the
# language means any language.
module_implementation_preferences
=
(
''
,)
def
__init__
(
self
,
play_context
,
new_stdin
,
*
args
,
**
kwargs
):
def
__init__
(
self
,
play_context
,
new_stdin
,
*
args
,
**
kwargs
):
# All these hasattrs allow subclasses to override these parameters
# All these hasattrs allow subclasses to override these parameters
...
...
lib/ansible/plugins/connection/winrm.py
View file @
4b0d52d2
...
@@ -52,10 +52,11 @@ from ansible.utils.unicode import to_bytes, to_unicode
...
@@ -52,10 +52,11 @@ from ansible.utils.unicode import to_bytes, to_unicode
class
Connection
(
ConnectionBase
):
class
Connection
(
ConnectionBase
):
'''WinRM connections over HTTP/HTTPS.'''
'''WinRM connections over HTTP/HTTPS.'''
module_implementation_preferences
=
(
'.ps1'
,
''
)
def
__init__
(
self
,
*
args
,
**
kwargs
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
has_pipelining
=
False
self
.
has_pipelining
=
False
self
.
default_suffixes
=
[
'.ps1'
,
''
]
self
.
protocol
=
None
self
.
protocol
=
None
self
.
shell_id
=
None
self
.
shell_id
=
None
self
.
delegate
=
None
self
.
delegate
=
None
...
...
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