Skip to content

Commit 504d738

Browse files
committedOct 29, 2020
Add dymanic blocks
1 parent ee68079 commit 504d738

File tree

13 files changed

+5094
-1
lines changed

13 files changed

+5094
-1
lines changed
 

‎lib/ansible/executor/task_executor.py

+10
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,16 @@ def _execute(self, variables=None):
583583
include_file = templar.template(include_file)
584584
return dict(include=include_file, include_args=include_args)
585585

586+
# if this task is a TaskDo, we just return now with a success code so the
587+
# main thread can expand the task list for the given host
588+
if self._task.action == 'do':
589+
do_args = self._task.args.copy()
590+
do_block = do_args.get('_raw_params', None)
591+
if not do_block:
592+
return dict(failed=True, msg="Do block was specified without a body.")
593+
594+
return dict(do=do_block, do_args=do_args)
595+
586596
# if this task is a IncludeRole, we just return now with a success code so the main thread can expand the task list for the given host
587597
elif self._task.action == 'include_role':
588598
include_args = self._task.args.copy()

‎lib/ansible/modules/do.py

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
# Copyright: Ansible Project
5+
# Copyright: Estelle Poulin <dev@inspiredby.es>
6+
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
7+
8+
from __future__ import absolute_import, division, print_function
9+
__metaclass__ = type
10+
11+
12+
DOCUMENTATION = r'''
13+
---
14+
author: Estelle Poulin (dev@inspiredby.es)
15+
module: do
16+
short_description: Dynamically include a task block
17+
description:
18+
- Includes a block with a list of tasks to be executed in the current playbook.
19+
version_added: '2.10'
20+
options:
21+
apply:
22+
description:
23+
- Accepts a hash of task keywords (e.g. C(tags), C(become)) that will be applied to the tasks within the include.
24+
type: str
25+
version_added: '2.7'
26+
free-form:
27+
description:
28+
- |
29+
Accepts a list of tasks specificed in the same manner as C(block).
30+
notes:
31+
- This is a core feature of the Ansible, rather than a module, and cannot be overridden like a module.
32+
seealso:
33+
- module: ansible.builtin.include
34+
- module: ansible.builtin.include_tasks
35+
- module: ansible.builtin.include_role
36+
- ref: playbooks_reuse_includes
37+
description: More information related to including and importing playbooks, roles and tasks.
38+
'''
39+
40+
EXAMPLES = r'''
41+
- hosts: all
42+
tasks:
43+
- debug:
44+
msg: task1
45+
46+
- name: Run a task list within a play.
47+
do:
48+
- debug:
49+
msg: stuff
50+
51+
- debug:
52+
msg: task10
53+
54+
- hosts: all
55+
tasks:
56+
- debug:
57+
msg: task1
58+
59+
- name: Run the task list only if the condition is true.
60+
do:
61+
- debug:
62+
msg: stuff
63+
when: hostvar is defined
64+
65+
- name: Apply tags to tasks within included file
66+
do:
67+
- debug:
68+
msg: stuff
69+
args:
70+
apply:
71+
tags: [install]
72+
tags: [always]
73+
74+
- name: Loop over a block of tasks.
75+
do:
76+
- debug:
77+
var: item
78+
loop: [1, 2, 3]
79+
80+
'''
81+
82+
RETURN = r'''
83+
# This module does not return anything except tasks to execute.
84+
'''

‎lib/ansible/parsing/mod_args.py

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from ansible.module_utils.six import iteritems, string_types
2424
from ansible.module_utils._text import to_text
2525
from ansible.parsing.splitter import parse_kv, split_args
26+
from ansible.parsing.yaml.objects import AnsibleSequence
2627
from ansible.plugins.loader import module_loader, action_loader
2728
from ansible.template import Templar
2829
from ansible.utils.sentinel import Sentinel
@@ -44,6 +45,7 @@
4445
'include_tasks',
4546
'include_role',
4647
'import_tasks',
48+
'do',
4749
'import_role',
4850
'add_host',
4951
'group_by',
@@ -208,6 +210,8 @@ def _normalize_new_style_args(self, thing, action):
208210
elif thing is None:
209211
# this can happen with modules which take no params, like ping:
210212
args = None
213+
elif isinstance(thing, AnsibleSequence):
214+
args = { u'_raw_params': thing }
211215
else:
212216
raise AnsibleParserError("unexpected parameter type in action: %s" % type(thing), obj=self._task_ds)
213217
return args

‎lib/ansible/playbook/do_block.py

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
2+
# (c) 2020, Estelle Poulin <dev@inspiredby.es>
3+
#
4+
# This file is part of Ansible
5+
#
6+
# Ansible is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU General Public License as published by
8+
# the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# Ansible is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
18+
19+
# Make coding more python3-ish
20+
from __future__ import (absolute_import, division, print_function)
21+
__metaclass__ = type
22+
23+
import os
24+
25+
from ansible.errors import AnsibleError
26+
from ansible.module_utils._text import to_text
27+
from ansible.playbook.task_include import TaskInclude
28+
from ansible.template import Templar
29+
from ansible.utils.display import Display
30+
31+
display = Display()
32+
33+
34+
class DoBlock:
35+
36+
def __init__(self, block, args, vars, task):
37+
self._block = block
38+
self._args = args
39+
self._vars = vars
40+
self._task = task
41+
self._hosts = []
42+
43+
def add_host(self, host):
44+
if host not in self._hosts:
45+
self._hosts.append(host)
46+
return
47+
raise ValueError()
48+
49+
def __eq__(self, other):
50+
return (other._args == self._args and
51+
other._vars == self._vars and
52+
other._task._uuid == self._task._uuid and
53+
other._task._parent._uuid == self._task._parent._uuid)
54+
55+
def __repr__(self):
56+
return "do_block (args=%s vars=%s): %s" % (self._args, self._vars, self._hosts)
57+
58+
@staticmethod
59+
def process_do_results(results, iterator, loader, variable_manager):
60+
do_blocks = []
61+
task_vars_cache = {}
62+
63+
for res in results:
64+
65+
original_host = res._host
66+
original_task = res._task
67+
68+
if original_task.action in ('do'):
69+
if original_task.loop:
70+
if 'results' not in res._result:
71+
continue
72+
do_results = res._result['results']
73+
else:
74+
do_results = [res._result]
75+
76+
for do_result in do_results:
77+
# if the task result was skipped or failed, continue
78+
if 'skipped' in do_result and do_result['skipped'] or 'failed' in do_result and do_result['failed']:
79+
continue
80+
81+
cache_key = (iterator._play, original_host, original_task)
82+
try:
83+
task_vars = task_vars_cache[cache_key]
84+
except KeyError:
85+
task_vars = task_vars_cache[cache_key] = variable_manager.get_vars(play=iterator._play, host=original_host, task=original_task)
86+
87+
do_args = do_result.get('do_args', dict())
88+
special_vars = {}
89+
loop_var = do_result.get('ansible_loop_var', 'item')
90+
index_var = do_result.get('ansible_index_var')
91+
if loop_var in do_result:
92+
task_vars[loop_var] = special_vars[loop_var] = do_result[loop_var]
93+
if index_var and index_var in do_result:
94+
task_vars[index_var] = special_vars[index_var] = do_result[index_var]
95+
if '_ansible_item_label' in do_result:
96+
task_vars['_ansible_item_label'] = special_vars['_ansible_item_label'] = do_result['_ansible_item_label']
97+
if 'ansible_loop' in do_result:
98+
task_vars['ansible_loop'] = special_vars['ansible_loop'] = do_result['ansible_loop']
99+
if original_task.no_log and '_ansible_no_log' not in do_args:
100+
task_vars['_ansible_no_log'] = special_vars['_ansible_no_log'] = original_task.no_log
101+
102+
# get search path for this task to pass to lookup plugins that may be used in pathing to
103+
# the do block
104+
task_vars['ansible_search_path'] = original_task.get_search_path()
105+
106+
# ensure basedir is always in (dwim already searches here but we need to display it)
107+
if loader.get_basedir() not in task_vars['ansible_search_path']:
108+
task_vars['ansible_search_path'].append(loader.get_basedir())
109+
110+
do_block = original_task
111+
112+
do_blk = DoBlock(do_block, do_args, special_vars, original_task)
113+
114+
idx = 0
115+
orig_do_blk = do_blk
116+
while 1:
117+
try:
118+
pos = do_blocks[idx:].index(orig_do_blk)
119+
# pos is relative to idx since we are slicing
120+
# use idx + pos due to relative indexing
121+
do_blk = do_blocks[idx + pos]
122+
except ValueError:
123+
do_blocks.append(orig_do_blk)
124+
do_blk = orig_do_blk
125+
126+
try:
127+
do_blk.add_host(original_host)
128+
except ValueError:
129+
# The host already exists for this do block, advance forward, this is a new do block
130+
idx += pos + 1
131+
else:
132+
break
133+
134+
return do_blocks

‎lib/ansible/playbook/task_include.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def get_vars(self):
104104
we need to include the args of the include into the vars as
105105
they are params to the included tasks. But ONLY for 'include'
106106
'''
107-
if self.action != 'include':
107+
if self.action not in ('include', 'do') :
108108
all_vars = super(TaskInclude, self).get_vars()
109109
else:
110110
all_vars = dict()

‎lib/ansible/plugins/strategy/__init__.py

+75
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,20 @@ def _copy_included_file(self, included_file):
778778

779779
return ti_copy
780780

781+
def _copy_do(self, do_block):
782+
'''
783+
A proven safe and performant way to create a copy of do block.
784+
'''
785+
ti_copy = do_block._task.copy(exclude_parent=True)
786+
ti_copy._parent = do_block._task._parent
787+
788+
temp_vars = ti_copy.vars.copy()
789+
temp_vars.update(do_block._vars)
790+
791+
ti_copy.vars = temp_vars
792+
793+
return ti_copy
794+
781795
def _load_included_file(self, included_file, iterator, is_handler=False):
782796
'''
783797
Loads an included YAML file of tasks, applying the optional set of variables.
@@ -843,6 +857,67 @@ def _load_included_file(self, included_file, iterator, is_handler=False):
843857
display.debug("done processing included file")
844858
return block_list
845859

860+
###
861+
def _load_do_block(self, do_block, iterator, is_handler=False):
862+
'''
863+
Loads a list of tasks from the args of a do block, applying the optional set of variables.
864+
'''
865+
866+
try:
867+
data = do_block._args['_raw_params']
868+
if data is None:
869+
return []
870+
elif not isinstance(data, list):
871+
raise AnsibleError("do blocks must contain a list of tasks")
872+
873+
ti_copy = self._copy_do(do_block)
874+
# pop tags out of the do args, if they were specified there, and assign
875+
# them to the block. If the do already had tags specified, we raise an
876+
# error so that users know not to specify them both ways
877+
tags = do_block._task.vars.pop('tags', [])
878+
if isinstance(tags, string_types):
879+
tags = tags.split(',')
880+
if len(tags) > 0:
881+
if len(do_block._task.tags) > 0:
882+
raise AnsibleParserError("Do blocks should not specify tags in more than one way (both via args and directly on the task). "
883+
"Mixing tag specify styles is prohibited for whole import hierarchy, not only for single import statement",
884+
obj=do_block._task._ds)
885+
display.deprecated("You should not specify tags in the do parameters. All tags should be specified using the task-level option",
886+
version='2.12', collection_name='ansible.builtin')
887+
do_block._task.tags = tags
888+
889+
block_list = load_list_of_blocks(
890+
data,
891+
play=iterator._play,
892+
parent_block=ti_copy,
893+
role=do_block._task._role,
894+
use_handlers=is_handler,
895+
loader=self._loader,
896+
variable_manager=self._variable_manager,
897+
)
898+
899+
# since we skip incrementing the stats when the task result is
900+
# first processed, we do so now for each host in the list
901+
for host in do_block._hosts:
902+
self._tqm._stats.increment('ok', host.name)
903+
904+
except AnsibleError as e:
905+
reason = to_text(e)
906+
907+
# mark all of the hosts including this file as failed, send callbacks,
908+
# and increment the stats for this host
909+
for host in do_block._hosts:
910+
tr = TaskResult(host=host, task=do_block._task, return_data=dict(failed=True, reason=reason))
911+
iterator.mark_host_failed(host)
912+
self._tqm._failed_hosts[host.name] = True
913+
self._tqm._stats.increment('failures', host.name)
914+
self._tqm.send_callback('v2_runner_on_failed', tr)
915+
return []
916+
917+
display.debug("done processing do blocks")
918+
return block_list
919+
920+
###
846921
def run_handlers(self, iterator, play_context):
847922
'''
848923
Runs handlers on those hosts which have been notified.

‎lib/ansible/plugins/strategy/linear.py

+63
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from ansible.module_utils._text import to_text
3838
from ansible.playbook.block import Block
3939
from ansible.playbook.included_file import IncludedFile
40+
from ansible.playbook.do_block import DoBlock
4041
from ansible.playbook.task import Task
4142
from ansible.plugins.loader import action_loader
4243
from ansible.plugins.strategy import StrategyBase
@@ -331,6 +332,13 @@ def run(self, iterator, play_context):
331332
variable_manager=self._variable_manager
332333
)
333334

335+
do_blocks = DoBlock.process_do_results(
336+
host_results,
337+
iterator=iterator,
338+
loader=self._loader,
339+
variable_manager=self._variable_manager
340+
)
341+
334342
include_failure = False
335343
if len(included_files) > 0:
336344
display.debug("we have included files to process")
@@ -391,6 +399,61 @@ def run(self, iterator, play_context):
391399
display.debug("done extending task lists")
392400
display.debug("done processing included files")
393401

402+
###
403+
if len(do_blocks) > 0:
404+
display.debug("we have included do blocks to process")
405+
406+
display.debug("generating all_blocks data")
407+
all_blocks = dict((host, []) for host in hosts_left)
408+
display.debug("done generating all_blocks data")
409+
for do_block in do_blocks:
410+
display.debug("processing do_block")
411+
# included hosts get the task list while those excluded get an equal-length
412+
# list of noop tasks, to make sure that they continue running in lock-step
413+
try:
414+
# TODO
415+
new_blocks = self._load_do_block(do_block, iterator=iterator)
416+
417+
display.debug("iterating over new_blocks loaded from do blocks")
418+
for new_block in new_blocks:
419+
task_vars = self._variable_manager.get_vars(
420+
play=iterator._play,
421+
task=new_block._parent
422+
)
423+
display.debug("filtering new block on tags")
424+
final_block = new_block.filter_tagged_tasks(task_vars)
425+
display.debug("done filtering new block on tags")
426+
427+
noop_block = self._prepare_and_create_noop_block_from(final_block, task._parent, iterator)
428+
429+
for host in hosts_left:
430+
# TODO
431+
if host in do_block._hosts:
432+
all_blocks[host].append(final_block)
433+
else:
434+
all_blocks[host].append(noop_block)
435+
display.debug("done iterating over new_blocks loaded from do block")
436+
437+
except AnsibleError as e:
438+
for host in do_block._hosts:
439+
self._tqm._failed_hosts[host.name] = True
440+
iterator.mark_host_failed(host)
441+
display.error(to_text(e), wrap_text=False)
442+
include_failure = True
443+
continue
444+
445+
# finally go through all of the hosts and append the
446+
# accumulated blocks to their list of tasks
447+
display.debug("extending task lists for all hosts with do blocks")
448+
449+
for host in hosts_left:
450+
iterator.add_tasks(host, all_blocks[host])
451+
452+
display.debug("done extending task lists")
453+
display.debug("done processing do blocks")
454+
455+
###
456+
394457
display.debug("results queue empty")
395458

396459
display.debug("checking for any_errors_fatal")

‎lib/ansible_base.egg-info/PKG-INFO

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
Metadata-Version: 1.2
2+
Name: ansible-base
3+
Version: 2.10.2.post0
4+
Summary: Radically simple IT automation
5+
Home-page: https://ansible.com/
6+
Author: Ansible, Inc.
7+
Author-email: info@ansible.com
8+
License: GPLv3+
9+
Project-URL: Bug Tracker, https://github.com/ansible/ansible/issues
10+
Project-URL: CI: Shippable, https://app.shippable.com/github/ansible/ansible
11+
Project-URL: Code of Conduct, https://docs.ansible.com/ansible/latest/community/code_of_conduct.html
12+
Project-URL: Documentation, https://docs.ansible.com/ansible/
13+
Project-URL: Mailing lists, https://docs.ansible.com/ansible/latest/community/communication.html#mailing-list-information
14+
Project-URL: Source Code, https://github.com/ansible/ansible
15+
Description: |PyPI version| |Docs badge| |Chat badge| |Build Status| |Code Of Conduct| |Mailing Lists| |License|
16+
17+
*******
18+
Ansible
19+
*******
20+
21+
Ansible is a radically simple IT automation system. It handles
22+
configuration management, application deployment, cloud provisioning,
23+
ad-hoc task execution, network automation, and multi-node orchestration. Ansible makes complex
24+
changes like zero-downtime rolling updates with load balancers easy. More information on `the Ansible website <https://ansible.com/>`_.
25+
26+
Design Principles
27+
=================
28+
29+
* Have a dead simple setup process and a minimal learning curve.
30+
* Manage machines very quickly and in parallel.
31+
* Avoid custom-agents and additional open ports, be agentless by
32+
leveraging the existing SSH daemon.
33+
* Describe infrastructure in a language that is both machine and human
34+
friendly.
35+
* Focus on security and easy auditability/review/rewriting of content.
36+
* Manage new remote machines instantly, without bootstrapping any
37+
software.
38+
* Allow module development in any dynamic language, not just Python.
39+
* Be usable as non-root.
40+
* Be the easiest IT automation system to use, ever.
41+
42+
Use Ansible
43+
===========
44+
45+
You can install a released version of Ansible via ``pip``, a package manager, or
46+
our `release repository <https://releases.ansible.com/ansible/>`_. See our
47+
`installation guide <https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html>`_ for details on installing Ansible
48+
on a variety of platforms.
49+
50+
Red Hat offers supported builds of `Ansible Engine <https://www.ansible.com/ansible-engine>`_.
51+
52+
Power users and developers can run the ``devel`` branch, which has the latest
53+
features and fixes, directly. Although it is reasonably stable, you are more likely to encounter
54+
breaking changes when running the ``devel`` branch. We recommend getting involved
55+
in the Ansible community if you want to run the ``devel`` branch.
56+
57+
Get Involved
58+
============
59+
60+
* Read `Community
61+
Information <https://docs.ansible.com/ansible/latest/community>`_ for all
62+
kinds of ways to contribute to and interact with the project,
63+
including mailing list information and how to submit bug reports and
64+
code to Ansible.
65+
* Join a `Working Group
66+
<https://github.com/ansible/community/wiki>`_, an organized community devoted to a specific technology domain or platform.
67+
* Submit a proposed code update through a pull request to the ``devel`` branch.
68+
* Talk to us before making larger changes
69+
to avoid duplicate efforts. This not only helps everyone
70+
know what is going on, it also helps save time and effort if we decide
71+
some changes are needed.
72+
* For a list of email lists, IRC channels and Working Groups, see the
73+
`Communication page <https://docs.ansible.com/ansible/latest/community/communication.html>`_
74+
75+
Coding Guidelines
76+
=================
77+
78+
We document our Coding Guidelines in the `Developer Guide <https://docs.ansible.com/ansible/devel/dev_guide/>`_. We particularly suggest you review:
79+
80+
* `Contributing your module to Ansible <https://docs.ansible.com/ansible/devel/dev_guide/developing_modules_checklist.html>`_
81+
* `Conventions, tips and pitfalls <https://docs.ansible.com/ansible/devel/dev_guide/developing_modules_best_practices.html>`_
82+
83+
Branch Info
84+
===========
85+
86+
* The ``devel`` branch corresponds to the release actively under development.
87+
* The ``stable-2.X`` branches correspond to stable releases.
88+
* Create a branch based on ``devel`` and set up a `dev environment <https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_general.html#common-environment-setup>`_ if you want to open a PR.
89+
* See the `Ansible release and maintenance <https://docs.ansible.com/ansible/latest/reference_appendices/release_and_maintenance.html>`_ page for information about active branches.
90+
91+
Roadmap
92+
=======
93+
94+
Based on team and community feedback, an initial roadmap will be published for a major or minor version (ex: 2.7, 2.8).
95+
The `Ansible Roadmap page <https://docs.ansible.com/ansible/devel/roadmap/>`_ details what is planned and how to influence the roadmap.
96+
97+
Authors
98+
=======
99+
100+
Ansible was created by `Michael DeHaan <https://github.com/mpdehaan>`_
101+
and has contributions from over 4700 users (and growing). Thanks everyone!
102+
103+
`Ansible <https://www.ansible.com>`_ is sponsored by `Red Hat, Inc.
104+
<https://www.redhat.com>`_
105+
106+
License
107+
=======
108+
109+
GNU General Public License v3.0 or later
110+
111+
See `COPYING <COPYING>`_ to see the full text.
112+
113+
.. |PyPI version| image:: https://img.shields.io/pypi/v/ansible.svg
114+
:target: https://pypi.org/project/ansible
115+
.. |Docs badge| image:: https://img.shields.io/badge/docs-latest-brightgreen.svg
116+
:target: https://docs.ansible.com/ansible/latest/
117+
.. |Build Status| image:: https://api.shippable.com/projects/573f79d02a8192902e20e34b/badge?branch=devel
118+
:target: https://app.shippable.com/projects/573f79d02a8192902e20e34b
119+
.. |Chat badge| image:: https://img.shields.io/badge/chat-IRC-brightgreen.svg
120+
:target: https://docs.ansible.com/ansible/latest/community/communication.html
121+
.. |Code Of Conduct| image:: https://img.shields.io/badge/code%20of%20conduct-Ansible-silver.svg
122+
:target: https://docs.ansible.com/ansible/latest/community/code_of_conduct.html
123+
:alt: Ansible Code of Conduct
124+
.. |Mailing Lists| image:: https://img.shields.io/badge/mailing%20lists-Ansible-orange.svg
125+
:target: https://docs.ansible.com/ansible/latest/community/communication.html#mailing-list-information
126+
:alt: Ansible mailing lists
127+
.. |License| image:: https://img.shields.io/badge/license-GPL%20v3.0-brightgreen.svg
128+
:target: COPYING
129+
:alt: Repository License
130+
131+
Platform: UNKNOWN
132+
Classifier: Development Status :: 5 - Production/Stable
133+
Classifier: Environment :: Console
134+
Classifier: Intended Audience :: Developers
135+
Classifier: Intended Audience :: Information Technology
136+
Classifier: Intended Audience :: System Administrators
137+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
138+
Classifier: Natural Language :: English
139+
Classifier: Operating System :: POSIX
140+
Classifier: Programming Language :: Python :: 2
141+
Classifier: Programming Language :: Python :: 2.7
142+
Classifier: Programming Language :: Python :: 3
143+
Classifier: Programming Language :: Python :: 3.5
144+
Classifier: Programming Language :: Python :: 3.6
145+
Classifier: Programming Language :: Python :: 3.7
146+
Classifier: Programming Language :: Python :: 3.8
147+
Classifier: Topic :: System :: Installation/Setup
148+
Classifier: Topic :: System :: Systems Administration
149+
Classifier: Topic :: Utilities
150+
Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*

‎lib/ansible_base.egg-info/SOURCES.txt

+4,565
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
jinja2
2+
PyYAML
3+
cryptography
4+
packaging
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ansible
2+
ansible_test

0 commit comments

Comments
 (0)
Please sign in to comment.