@ -10,11 +10,97 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
__metaclass__ = type
import os
import os
import stat
import platform
import platform
import subprocess
import sys
import sys
from ansible . module_utils . basic import AnsibleModule
from ansible . module_utils . basic import AnsibleModule
# trace_realpath() and _join_tracepath() adapated from stdlib posixpath.py
# https://github.com/python/cpython/blob/v3.12.6/Lib/posixpath.py#L423-L492
# Copyright (c) 2001 - 2023 Python Software Foundation
# Copyright (c) 2024 Alex Willmer <alex@moreati.org.uk>
# License: Python Software Foundation License Version 2
def trace_realpath ( filename , strict = False ) :
"""
Return the canonical path of the specified filename , and a trace of
the route taken , eliminating any symbolic links encountered in the path .
"""
path , trace , ok = _join_tracepath ( filename [ : 0 ] , filename , strict , seen = { } , trace = [ ] )
return os . path . abspath ( path ) , trace
def _join_tracepath ( path , rest , strict , seen , trace ) :
"""
Join two paths , normalizing and eliminating any symbolic links encountered
in the second path .
"""
trace . append ( rest )
if isinstance ( path , bytes ) :
sep = b ' / '
curdir = b ' . '
pardir = b ' .. '
else :
sep = ' / '
curdir = ' . '
pardir = ' .. '
if os . path . isabs ( rest ) :
rest = rest [ 1 : ]
path = sep
while rest :
name , _ , rest = rest . partition ( sep )
if not name or name == curdir :
# current dir
continue
if name == pardir :
# parent dir
if path :
path , name = os . path . split ( path )
if name == pardir :
path = os . path . join ( path , pardir , pardir )
else :
path = pardir
continue
newpath = os . path . join ( path , name )
try :
st = os . lstat ( newpath )
except OSError :
if strict :
raise
is_link = False
else :
is_link = stat . S_ISLNK ( st . st_mode )
if not is_link :
path = newpath
continue
# Resolve the symbolic link
if newpath in seen :
# Already seen this path
path = seen [ newpath ]
if path is not None :
# use cached value
continue
# The symlink is not resolved, so we must have a symlink loop.
if strict :
# Raise OSError(errno.ELOOP)
os . stat ( newpath )
else :
# Return already resolved part + rest of the path unchanged.
return os . path . join ( newpath , rest ) , trace , False
seen [ newpath ] = None # not resolved symlink
path , trace , ok = _join_tracepath ( path , os . readlink ( newpath ) , strict , seen , trace )
if not ok :
return os . path . join ( path , rest ) , False
seen [ newpath ] = path # resolved symlink
return path , trace , True
def main ( ) :
def main ( ) :
module = AnsibleModule ( argument_spec = dict (
module = AnsibleModule ( argument_spec = dict (
facts_copy = dict ( type = dict , default = { } ) ,
facts_copy = dict ( type = dict , default = { } ) ,
@ -33,7 +119,18 @@ def main():
sys . executable = " /usr/bin/python "
sys . executable = " /usr/bin/python "
facts_copy = module . params [ ' facts_copy ' ]
facts_copy = module . params [ ' facts_copy ' ]
discovered_interpreter_python = facts_copy [ ' discovered_interpreter_python ' ]
discovered_interpreter_python = facts_copy [ ' discovered_interpreter_python ' ]
d_i_p_realpath , d_i_p_trace = trace_realpath ( discovered_interpreter_python )
d_i_p_proc = subprocess . Popen (
[ discovered_interpreter_python , ' -c ' , ' import sys; print(sys.executable) ' ] ,
stdout = subprocess . PIPE , stderr = subprocess . PIPE ,
)
d_i_p_stdout , d_i_p_stderr = d_i_p_proc . communicate ( )
sys_exec_realpath , sys_exec_trace = trace_realpath ( sys . executable )
result = {
result = {
' changed ' : False ,
' changed ' : False ,
' ansible_facts ' : module . params [ ' facts_to_override ' ] ,
' ansible_facts ' : module . params [ ' facts_to_override ' ] ,
@ -43,7 +140,17 @@ def main():
) ,
) ,
' discovered_python ' : {
' discovered_python ' : {
' as_seen ' : discovered_interpreter_python ,
' as_seen ' : discovered_interpreter_python ,
' resolved ' : os . path . realpath ( discovered_interpreter_python ) ,
' resolved ' : d_i_p_realpath ,
' trace ' : [ os . path . abspath ( p ) for p in d_i_p_trace ] ,
' sys ' : {
' executable ' : {
' as_seen ' : d_i_p_stdout . decode ( ' ascii ' ) . rstrip ( ' \n ' ) ,
' proc ' : {
' stderr ' : d_i_p_stderr . decode ( ' ascii ' ) ,
' returncode ' : d_i_p_proc . returncode ,
} ,
} ,
} ,
} ,
} ,
' running_python ' : {
' running_python ' : {
' platform ' : {
' platform ' : {
@ -54,7 +161,8 @@ def main():
' sys ' : {
' sys ' : {
' executable ' : {
' executable ' : {
' as_seen ' : sys . executable ,
' as_seen ' : sys . executable ,
' resolved ' : os . path . realpath ( sys . executable ) ,
' resolved ' : sys_exec_realpath ,
' trace ' : [ os . path . abspath ( p ) for p in sys_exec_trace ] ,
} ,
} ,
' platform ' : sys . platform ,
' platform ' : sys . platform ,
' version_info ' : {
' version_info ' : {