Skip to content

Commit e70c316

Browse files
committed
Merge remote-tracking branch 'origin/dmw'
2 parents 99c5cec + d6329f3 commit e70c316

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+343
-553
lines changed

.ci/ci_lib.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ def start_containers(containers):
264264
"docker rm -f %(name)s || true" % container,
265265
"docker run "
266266
"--rm "
267-
"--cpuset-cpus 0,1 "
267+
# "--cpuset-cpus 0,1 "
268268
"--detach "
269269
"--privileged "
270270
"--cap-add=SYS_PTRACE "

.ci/mitogen_py24_install.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
'docker pull %s' % (ci_lib.image_for_distro(ci_lib.DISTRO),),
88
],
99
[
10-
'sudo tar -C / -jxvf tests/data/ubuntu-python-2.4.6.tar.bz2',
10+
'curl https://dw.github.io/mitogen/binaries/ubuntu-python-2.4.6.tar.bz2 | sudo tar -C / -jxv',
1111
]
1212
]
1313

ansible_mitogen/affinity.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,19 @@ def _mask_to_bytes(self, mask):
265265
mask >>= 64
266266
return mitogen.core.b('').join(chunks)
267267

268+
def _get_thread_ids(self):
269+
try:
270+
ents = os.listdir('/proc/self/task')
271+
except OSError:
272+
LOG.debug('cannot fetch thread IDs for current process')
273+
return [os.getpid()]
274+
275+
return [int(s) for s in ents if s.isdigit()]
276+
268277
def _set_cpu_mask(self, mask):
269278
s = self._mask_to_bytes(mask)
270-
_sched_setaffinity(os.getpid(), len(s), s)
279+
for tid in self._get_thread_ids():
280+
_sched_setaffinity(tid, len(s), s)
271281

272282

273283
if _sched_setaffinity is not None:

ansible_mitogen/connection.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,9 @@ def _connect(self):
791791

792792
inventory_name, stack = self._build_stack()
793793
worker_model = ansible_mitogen.process.get_worker_model()
794-
self.binding = worker_model.get_binding(inventory_name)
794+
self.binding = worker_model.get_binding(
795+
mitogen.utils.cast(inventory_name)
796+
)
795797
self._connect_stack(stack)
796798

797799
def _put_connection(self):

ansible_mitogen/loaders.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,8 @@
5555
from ansible.plugins import module_utils_loader
5656
from ansible.plugins import shell_loader
5757
from ansible.plugins import strategy_loader
58+
59+
60+
# These are original, unwrapped implementations
61+
action_loader__get = action_loader.get
62+
connection_loader__get = connection_loader.get

ansible_mitogen/logging.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,9 @@ def setup():
107107
l_mitogen = logging.getLogger('mitogen')
108108
l_mitogen_io = logging.getLogger('mitogen.io')
109109
l_ansible_mitogen = logging.getLogger('ansible_mitogen')
110+
l_operon = logging.getLogger('operon')
110111

111-
for logger in l_mitogen, l_mitogen_io, l_ansible_mitogen:
112+
for logger in l_mitogen, l_mitogen_io, l_ansible_mitogen, l_operon:
112113
logger.handlers = [Handler(display.vvv)]
113114
logger.propagate = False
114115

ansible_mitogen/mixins.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@
5555
import ansible_mitogen.target
5656
from ansible.module_utils._text import to_text
5757

58+
try:
59+
from ansible.utils.unsafe_proxy import wrap_var
60+
except ImportError:
61+
from ansible.vars.unsafe_proxy import wrap_var
62+
5863

5964
LOG = logging.getLogger(__name__)
6065

@@ -306,7 +311,7 @@ def get_task_timeout_secs(self):
306311
except AttributeError:
307312
return getattr(self._task, 'async')
308313

309-
def _temp_file_gibberish(self, module_args, wrap_async):
314+
def _set_temp_file_args(self, module_args, wrap_async):
310315
# Ansible>2.5 module_utils reuses the action's temporary directory if
311316
# one exists. Older versions error if this key is present.
312317
if ansible.__version__ > '2.5':
@@ -343,7 +348,7 @@ def _execute_module(self, module_name=None, module_args=None, tmp=None,
343348
self._update_module_args(module_name, module_args, task_vars)
344349
env = {}
345350
self._compute_environment_string(env)
346-
self._temp_file_gibberish(module_args, wrap_async)
351+
self._set_temp_file_args(module_args, wrap_async)
347352

348353
self._connection._connect()
349354
result = ansible_mitogen.planner.invoke(
@@ -365,7 +370,7 @@ def _execute_module(self, module_name=None, module_args=None, tmp=None,
365370
# on _execute_module().
366371
self._remove_tmp_path(tmp)
367372

368-
return result
373+
return wrap_var(result)
369374

370375
def _postprocess_response(self, result):
371376
"""

ansible_mitogen/planner.py

Lines changed: 102 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
from ansible.executor import module_common
4646
import ansible.errors
4747
import ansible.module_utils
48+
import ansible.release
4849
import mitogen.core
4950
import mitogen.select
5051

@@ -58,6 +59,8 @@
5859
NO_INTERPRETER_MSG = 'module (%s) is missing interpreter line'
5960
NO_MODULE_MSG = 'The module %s was not found in configured module paths.'
6061

62+
_planner_by_path = {}
63+
6164

6265
class Invocation(object):
6366
"""
@@ -92,7 +95,12 @@ def __init__(self, action, connection, module_name, module_args,
9295
self.module_path = None
9396
#: Initially ``None``, but set by :func:`invoke`. The raw source or
9497
#: binary contents of the module.
95-
self.module_source = None
98+
self._module_source = None
99+
100+
def get_module_source(self):
101+
if self._module_source is None:
102+
self._module_source = read_file(self.module_path)
103+
return self._module_source
96104

97105
def __repr__(self):
98106
return 'Invocation(module_name=%s)' % (self.module_name,)
@@ -107,7 +115,8 @@ class Planner(object):
107115
def __init__(self, invocation):
108116
self._inv = invocation
109117

110-
def detect(self):
118+
@classmethod
119+
def detect(cls, path, source):
111120
"""
112121
Return true if the supplied `invocation` matches the module type
113122
implemented by this planner.
@@ -171,8 +180,9 @@ class BinaryPlanner(Planner):
171180
"""
172181
runner_name = 'BinaryRunner'
173182

174-
def detect(self):
175-
return module_common._is_binary(self._inv.module_source)
183+
@classmethod
184+
def detect(cls, path, source):
185+
return module_common._is_binary(source)
176186

177187
def get_push_files(self):
178188
return [mitogen.core.to_text(self._inv.module_path)]
@@ -218,7 +228,7 @@ def _rewrite_interpreter(self, path):
218228

219229
def _get_interpreter(self):
220230
path, arg = ansible_mitogen.parsing.parse_hashbang(
221-
self._inv.module_source
231+
self._inv.get_module_source()
222232
)
223233
if path is None:
224234
raise ansible.errors.AnsibleError(NO_INTERPRETER_MSG % (
@@ -247,8 +257,9 @@ class JsonArgsPlanner(ScriptPlanner):
247257
"""
248258
runner_name = 'JsonArgsRunner'
249259

250-
def detect(self):
251-
return module_common.REPLACER_JSONARGS in self._inv.module_source
260+
@classmethod
261+
def detect(cls, path, source):
262+
return module_common.REPLACER_JSONARGS in source
252263

253264

254265
class WantJsonPlanner(ScriptPlanner):
@@ -265,8 +276,9 @@ class WantJsonPlanner(ScriptPlanner):
265276
"""
266277
runner_name = 'WantJsonRunner'
267278

268-
def detect(self):
269-
return b'WANT_JSON' in self._inv.module_source
279+
@classmethod
280+
def detect(cls, path, source):
281+
return b'WANT_JSON' in source
270282

271283

272284
class NewStylePlanner(ScriptPlanner):
@@ -278,8 +290,9 @@ class NewStylePlanner(ScriptPlanner):
278290
runner_name = 'NewStyleRunner'
279291
marker = b'from ansible.module_utils.'
280292

281-
def detect(self):
282-
return self.marker in self._inv.module_source
293+
@classmethod
294+
def detect(cls, path, source):
295+
return cls.marker in source
283296

284297
def _get_interpreter(self):
285298
return None, None
@@ -323,7 +336,6 @@ def get_search_path(self):
323336
for path in ansible_mitogen.loaders.module_utils_loader._get_paths(
324337
subdirs=False
325338
)
326-
if os.path.isdir(path)
327339
)
328340

329341
_module_map = None
@@ -347,6 +359,10 @@ def get_module_map(self):
347359
def get_kwargs(self):
348360
return super(NewStylePlanner, self).get_kwargs(
349361
module_map=self.get_module_map(),
362+
py_module_name=py_modname_from_path(
363+
self._inv.module_name,
364+
self._inv.module_path,
365+
),
350366
)
351367

352368

@@ -376,14 +392,16 @@ class ReplacerPlanner(NewStylePlanner):
376392
"""
377393
runner_name = 'ReplacerRunner'
378394

379-
def detect(self):
380-
return module_common.REPLACER in self._inv.module_source
395+
@classmethod
396+
def detect(cls, path, source):
397+
return module_common.REPLACER in source
381398

382399

383400
class OldStylePlanner(ScriptPlanner):
384401
runner_name = 'OldStyleRunner'
385402

386-
def detect(self):
403+
@classmethod
404+
def detect(cls, path, source):
387405
# Everything else.
388406
return True
389407

@@ -398,14 +416,54 @@ def detect(self):
398416
]
399417

400418

401-
def get_module_data(name):
402-
path = ansible_mitogen.loaders.module_loader.find_plugin(name, '')
403-
if path is None:
404-
raise ansible.errors.AnsibleError(NO_MODULE_MSG % (name,))
419+
try:
420+
_get_ansible_module_fqn = module_common._get_ansible_module_fqn
421+
except AttributeError:
422+
_get_ansible_module_fqn = None
423+
405424

406-
with open(path, 'rb') as fp:
407-
source = fp.read()
408-
return mitogen.core.to_text(path), source
425+
def py_modname_from_path(name, path):
426+
"""
427+
Fetch the logical name of a new-style module as it might appear in
428+
:data:`sys.modules` of the target's Python interpreter.
429+
430+
* For Ansible <2.7, this is an unpackaged module named like
431+
"ansible_module_%s".
432+
433+
* For Ansible <2.9, this is an unpackaged module named like
434+
"ansible.modules.%s"
435+
436+
* Since Ansible 2.9, modules appearing within a package have the original
437+
package hierarchy approximated on the target, enabling relative imports
438+
to function correctly. For example, "ansible.modules.system.setup".
439+
"""
440+
# 2.9+
441+
if _get_ansible_module_fqn:
442+
try:
443+
return _get_ansible_module_fqn(path)
444+
except ValueError:
445+
pass
446+
447+
if ansible.__version__ < '2.7':
448+
return 'ansible_module_' + name
449+
450+
return 'ansible.modules.' + name
451+
452+
453+
def read_file(path):
454+
fd = os.open(path, os.O_RDONLY)
455+
try:
456+
bits = []
457+
chunk = True
458+
while True:
459+
chunk = os.read(fd, 65536)
460+
if not chunk:
461+
break
462+
bits.append(chunk)
463+
finally:
464+
os.close(fd)
465+
466+
return mitogen.core.b('').join(bits)
409467

410468

411469
def _propagate_deps(invocation, planner, context):
@@ -466,14 +524,12 @@ def _invoke_isolated_task(invocation, planner):
466524
context.shutdown()
467525

468526

469-
def _get_planner(invocation):
527+
def _get_planner(name, path, source):
470528
for klass in _planners:
471-
planner = klass(invocation)
472-
if planner.detect():
473-
LOG.debug('%r accepted %r (filename %r)', planner,
474-
invocation.module_name, invocation.module_path)
475-
return planner
476-
LOG.debug('%r rejected %r', planner, invocation.module_name)
529+
if klass.detect(path, source):
530+
LOG.debug('%r accepted %r (filename %r)', klass, name, path)
531+
return klass
532+
LOG.debug('%r rejected %r', klass, name)
477533
raise ansible.errors.AnsibleError(NO_METHOD_MSG + repr(invocation))
478534

479535

@@ -488,10 +544,24 @@ def invoke(invocation):
488544
:raises ansible.errors.AnsibleError:
489545
Unrecognized/unsupported module type.
490546
"""
491-
(invocation.module_path,
492-
invocation.module_source) = get_module_data(invocation.module_name)
493-
planner = _get_planner(invocation)
547+
path = ansible_mitogen.loaders.module_loader.find_plugin(
548+
invocation.module_name,
549+
'',
550+
)
551+
if path is None:
552+
raise ansible.errors.AnsibleError(NO_MODULE_MSG % (
553+
invocation.module_name,
554+
))
555+
556+
invocation.module_path = mitogen.core.to_text(path)
557+
if invocation.module_path not in _planner_by_path:
558+
_planner_by_path[invocation.module_path] = _get_planner(
559+
invocation.module_name,
560+
invocation.module_path,
561+
invocation.get_module_source()
562+
)
494563

564+
planner = _planner_by_path[invocation.module_path](invocation)
495565
if invocation.wrap_async:
496566
response = _invoke_async_task(invocation, planner)
497567
elif planner.should_fork():

ansible_mitogen/plugins/connection/mitogen_kubectl.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,6 @@
3131
import os.path
3232
import sys
3333

34-
try:
35-
from ansible.plugins.connection import kubectl
36-
except ImportError:
37-
kubectl = None
38-
3934
from ansible.errors import AnsibleConnectionFailure
4035
from ansible.module_utils.six import iteritems
4136

@@ -47,6 +42,19 @@
4742
del base_dir
4843

4944
import ansible_mitogen.connection
45+
import ansible_mitogen.loaders
46+
47+
48+
_class = ansible_mitogen.loaders.connection_loader__get(
49+
'kubectl',
50+
class_only=True,
51+
)
52+
53+
if _class:
54+
kubectl = sys.modules[_class.__module__]
55+
del _class
56+
else:
57+
kubectl = None
5058

5159

5260
class Connection(ansible_mitogen.connection.Connection):

0 commit comments

Comments
 (0)