ansible-core 2.15.2__py3-none-any.whl → 2.15.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ansible-core might be problematic. Click here for more details.

Files changed (53) hide show
  1. ansible/cli/config.py +1 -0
  2. ansible/cli/galaxy.py +20 -3
  3. ansible/cli/inventory.py +1 -1
  4. ansible/executor/task_executor.py +1 -2
  5. ansible/galaxy/collection/__init__.py +3 -1
  6. ansible/module_utils/ansible_release.py +1 -1
  7. ansible/module_utils/urls.py +16 -12
  8. ansible/modules/getent.py +1 -1
  9. ansible/modules/git.py +1 -1
  10. ansible/modules/setup.py +1 -1
  11. ansible/modules/stat.py +3 -3
  12. ansible/modules/validate_argument_spec.py +1 -1
  13. ansible/modules/yum.py +1 -1
  14. ansible/plugins/action/__init__.py +1 -1
  15. ansible/plugins/filter/split.yml +1 -1
  16. ansible/release.py +1 -1
  17. ansible/utils/encrypt.py +9 -2
  18. ansible_core-2.15.3.dist-info/METADATA +129 -0
  19. {ansible_core-2.15.2.dist-info → ansible_core-2.15.3.dist-info}/RECORD +51 -49
  20. {ansible_core-2.15.2.dist-info → ansible_core-2.15.3.dist-info}/WHEEL +1 -1
  21. ansible_test/_data/requirements/sanity.ansible-doc.txt +2 -0
  22. ansible_test/_data/requirements/sanity.changelog.txt +2 -0
  23. ansible_test/_data/requirements/sanity.import.plugin.txt +2 -0
  24. ansible_test/_data/requirements/sanity.import.txt +2 -0
  25. ansible_test/_data/requirements/sanity.integration-aliases.txt +2 -0
  26. ansible_test/_data/requirements/sanity.pylint.txt +2 -0
  27. ansible_test/_data/requirements/sanity.runtime-metadata.txt +2 -0
  28. ansible_test/_data/requirements/sanity.validate-modules.txt +2 -0
  29. ansible_test/_data/requirements/sanity.yamllint.txt +2 -0
  30. ansible_test/_internal/ansible_util.py +1 -1
  31. ansible_test/_internal/classification/__init__.py +6 -13
  32. ansible_test/_internal/classification/python.py +0 -1
  33. ansible_test/_internal/commands/integration/cloud/cs.py +1 -1
  34. ansible_test/_internal/commands/sanity/__init__.py +44 -7
  35. ansible_test/_internal/commands/sanity/ansible_doc.py +3 -0
  36. ansible_test/_internal/commands/sanity/bin_symlinks.py +102 -0
  37. ansible_test/_internal/commands/sanity/import.py +1 -1
  38. ansible_test/_internal/commands/sanity/integration_aliases.py +430 -0
  39. ansible_test/_internal/commands/sanity/mypy.py +6 -1
  40. ansible_test/_internal/provider/layout/ansible.py +1 -1
  41. ansible_test/_internal/provider/source/unversioned.py +0 -3
  42. ansible_test/_internal/python_requirements.py +7 -0
  43. ansible_test/_internal/util.py +1 -1
  44. ansible_test/_internal/util_common.py +7 -2
  45. ansible_test/_util/controller/sanity/mypy/packaging.ini +20 -0
  46. ansible_test/_util/target/setup/ConfigureRemotingForAnsible.ps1 +1 -1
  47. ansible_test/_util/target/setup/requirements.py +63 -0
  48. ansible_core-2.15.2.dist-info/METADATA +0 -155
  49. ansible_test/_internal/commands/sanity/sanity_docs.py +0 -61
  50. {ansible_core-2.15.2.data → ansible_core-2.15.3.data}/scripts/ansible-test +0 -0
  51. {ansible_core-2.15.2.dist-info → ansible_core-2.15.3.dist-info}/COPYING +0 -0
  52. {ansible_core-2.15.2.dist-info → ansible_core-2.15.3.dist-info}/entry_points.txt +0 -0
  53. {ansible_core-2.15.2.dist-info → ansible_core-2.15.3.dist-info}/top_level.txt +0 -0
ansible/cli/config.py CHANGED
@@ -362,6 +362,7 @@ class ConfigCLI(CLI):
362
362
  return sections
363
363
 
364
364
  def execute_init(self):
365
+ """Create initial configuration"""
365
366
 
366
367
  data = []
367
368
  config_entries = self._list_entries_from_args()
ansible/cli/galaxy.py CHANGED
@@ -10,6 +10,7 @@ __metaclass__ = type
10
10
  # ansible.cli needs to be imported first, to ensure the source bin/* scripts run that code first
11
11
  from ansible.cli import CLI
12
12
 
13
+ import functools
13
14
  import json
14
15
  import os.path
15
16
  import pathlib
@@ -93,6 +94,7 @@ def with_collection_artifacts_manager(wrapped_method):
93
94
  the related temporary directory auto-cleanup around the target
94
95
  method invocation.
95
96
  """
97
+ @functools.wraps(wrapped_method)
96
98
  def method_wrapper(*args, **kwargs):
97
99
  if 'artifacts_manager' in kwargs:
98
100
  return wrapped_method(*args, **kwargs)
@@ -287,6 +289,7 @@ class GalaxyCLI(CLI):
287
289
 
288
290
  # Add sub parser for the Galaxy collection actions
289
291
  collection = type_parser.add_parser('collection', help='Manage an Ansible Galaxy collection.')
292
+ collection.set_defaults(func=self.execute_collection) # to satisfy doc build
290
293
  collection_parser = collection.add_subparsers(metavar='COLLECTION_ACTION', dest='action')
291
294
  collection_parser.required = True
292
295
  self.add_download_options(collection_parser, parents=[common, cache_options])
@@ -299,6 +302,7 @@ class GalaxyCLI(CLI):
299
302
 
300
303
  # Add sub parser for the Galaxy role actions
301
304
  role = type_parser.add_parser('role', help='Manage an Ansible Galaxy role.')
305
+ role.set_defaults(func=self.execute_role) # to satisfy doc build
302
306
  role_parser = role.add_subparsers(metavar='ROLE_ACTION', dest='action')
303
307
  role_parser.required = True
304
308
  self.add_init_options(role_parser, parents=[common, force, offline])
@@ -1018,6 +1022,7 @@ class GalaxyCLI(CLI):
1018
1022
 
1019
1023
  @with_collection_artifacts_manager
1020
1024
  def execute_download(self, artifacts_manager=None):
1025
+ """Download collections and their dependencies as a tarball for an offline install."""
1021
1026
  collections = context.CLIARGS['args']
1022
1027
  no_deps = context.CLIARGS['no_deps']
1023
1028
  download_path = context.CLIARGS['download_path']
@@ -1249,6 +1254,7 @@ class GalaxyCLI(CLI):
1249
1254
 
1250
1255
  @with_collection_artifacts_manager
1251
1256
  def execute_verify(self, artifacts_manager=None):
1257
+ """Compare checksums with the collection(s) found on the server and the installed copy. This does not verify dependencies."""
1252
1258
 
1253
1259
  collections = context.CLIARGS['args']
1254
1260
  search_paths = AnsibleCollectionConfig.collection_paths
@@ -1286,8 +1292,6 @@ class GalaxyCLI(CLI):
1286
1292
  You can pass in a list (roles or collections) or use the file
1287
1293
  option listed below (these are mutually exclusive). If you pass in a list, it
1288
1294
  can be a name (which will be downloaded via the galaxy API and github), or it can be a local tar archive file.
1289
-
1290
- :param artifacts_manager: Artifacts manager.
1291
1295
  """
1292
1296
  install_items = context.CLIARGS['args']
1293
1297
  requirements_file = context.CLIARGS['requirements']
@@ -1393,7 +1397,19 @@ class GalaxyCLI(CLI):
1393
1397
  upgrade = context.CLIARGS.get('upgrade', False)
1394
1398
 
1395
1399
  collections_path = C.COLLECTIONS_PATHS
1396
- if len([p for p in collections_path if p.startswith(path)]) == 0:
1400
+
1401
+ managed_paths = set(validate_collection_path(p) for p in C.COLLECTIONS_PATHS)
1402
+ read_req_paths = set(validate_collection_path(p) for p in AnsibleCollectionConfig.collection_paths)
1403
+
1404
+ unexpected_path = not any(p.startswith(path) for p in managed_paths)
1405
+ if unexpected_path and any(p.startswith(path) for p in read_req_paths):
1406
+ display.warning(
1407
+ f"The specified collections path '{path}' appears to be part of the pip Ansible package. "
1408
+ "Managing these directly with ansible-galaxy could break the Ansible package. "
1409
+ "Install collections to a configured collections path, which will take precedence over "
1410
+ "collections found in the PYTHONPATH."
1411
+ )
1412
+ elif unexpected_path:
1397
1413
  display.warning("The specified collections path '%s' is not part of the configured Ansible "
1398
1414
  "collections paths '%s'. The installed collection will not be picked up in an Ansible "
1399
1415
  "run, unless within a playbook-adjacent collections directory." % (to_text(path), to_text(":".join(collections_path))))
@@ -1410,6 +1426,7 @@ class GalaxyCLI(CLI):
1410
1426
  artifacts_manager=artifacts_manager,
1411
1427
  disable_gpg_verify=disable_gpg_verify,
1412
1428
  offline=context.CLIARGS.get('offline', False),
1429
+ read_requirement_paths=read_req_paths,
1413
1430
  )
1414
1431
 
1415
1432
  return 0
ansible/cli/inventory.py CHANGED
@@ -64,7 +64,7 @@ class InventoryCLI(CLI):
64
64
  def init_parser(self):
65
65
  super(InventoryCLI, self).init_parser(
66
66
  usage='usage: %prog [options] [host|group]',
67
- epilog='Show Ansible inventory information, by default it uses the inventory script JSON format')
67
+ desc='Show Ansible inventory information, by default it uses the inventory script JSON format')
68
68
 
69
69
  opt_help.add_inventory_options(self.parser)
70
70
  opt_help.add_vault_options(self.parser)
@@ -609,9 +609,8 @@ class TaskExecutor:
609
609
  display.vvvv('local domain socket path is %s' % socket_path, host=self._play_context.remote_addr)
610
610
  setattr(self._connection, '_socket_path', socket_path)
611
611
 
612
- # TODO: eventually remove this block as this should be a 'consequence' of 'forced_local' modules
613
612
  # special handling for python interpreter for network_os, default to ansible python unless overridden
614
- if 'ansible_network_os' in cvars and 'ansible_python_interpreter' not in cvars:
613
+ if 'ansible_python_interpreter' not in cvars and 'ansible_network_os' in cvars and getattr(self._connection, '_remote_is_local', False):
615
614
  # this also avoids 'python discovery'
616
615
  cvars['ansible_python_interpreter'] = sys.executable
617
616
 
@@ -654,6 +654,7 @@ def install_collections(
654
654
  artifacts_manager, # type: ConcreteArtifactsManager
655
655
  disable_gpg_verify, # type: bool
656
656
  offline, # type: bool
657
+ read_requirement_paths, # type: set[str]
657
658
  ): # type: (...) -> None
658
659
  """Install Ansible collections to the path specified.
659
660
 
@@ -668,7 +669,8 @@ def install_collections(
668
669
  """
669
670
  existing_collections = {
670
671
  Requirement(coll.fqcn, coll.ver, coll.src, coll.type, None)
671
- for coll in find_existing_collections(output_path, artifacts_manager)
672
+ for path in {output_path} | read_requirement_paths
673
+ for coll in find_existing_collections(path, artifacts_manager)
672
674
  }
673
675
 
674
676
  unsatisfied_requirements = set(
@@ -19,6 +19,6 @@
19
19
  from __future__ import (absolute_import, division, print_function)
20
20
  __metaclass__ = type
21
21
 
22
- __version__ = '2.15.2'
22
+ __version__ = '2.15.3'
23
23
  __author__ = 'Ansible, Inc.'
24
24
  __codename__ = "Ten Years Gone"
@@ -535,15 +535,18 @@ HTTPSClientAuthHandler = None
535
535
  UnixHTTPSConnection = None
536
536
  if hasattr(httplib, 'HTTPSConnection') and hasattr(urllib_request, 'HTTPSHandler'):
537
537
  class CustomHTTPSConnection(httplib.HTTPSConnection): # type: ignore[no-redef]
538
- def __init__(self, *args, **kwargs):
538
+ def __init__(self, client_cert=None, client_key=None, *args, **kwargs):
539
539
  httplib.HTTPSConnection.__init__(self, *args, **kwargs)
540
540
  self.context = None
541
541
  if HAS_SSLCONTEXT:
542
542
  self.context = self._context
543
543
  elif HAS_URLLIB3_PYOPENSSLCONTEXT:
544
544
  self.context = self._context = PyOpenSSLContext(PROTOCOL)
545
- if self.context and self.cert_file:
546
- self.context.load_cert_chain(self.cert_file, self.key_file)
545
+
546
+ self._client_cert = client_cert
547
+ self._client_key = client_key
548
+ if self.context and self._client_cert:
549
+ self.context.load_cert_chain(self._client_cert, self._client_key)
547
550
 
548
551
  def connect(self):
549
552
  "Connect to a host on a given (SSL) port."
@@ -564,10 +567,10 @@ if hasattr(httplib, 'HTTPSConnection') and hasattr(urllib_request, 'HTTPSHandler
564
567
  if HAS_SSLCONTEXT or HAS_URLLIB3_PYOPENSSLCONTEXT:
565
568
  self.sock = self.context.wrap_socket(sock, server_hostname=server_hostname)
566
569
  elif HAS_URLLIB3_SSL_WRAP_SOCKET:
567
- self.sock = ssl_wrap_socket(sock, keyfile=self.key_file, cert_reqs=ssl.CERT_NONE, # pylint: disable=used-before-assignment
568
- certfile=self.cert_file, ssl_version=PROTOCOL, server_hostname=server_hostname)
570
+ self.sock = ssl_wrap_socket(sock, keyfile=self._client_key, cert_reqs=ssl.CERT_NONE, # pylint: disable=used-before-assignment
571
+ certfile=self._client_cert, ssl_version=PROTOCOL, server_hostname=server_hostname)
569
572
  else:
570
- self.sock = ssl.wrap_socket(sock, keyfile=self.key_file, certfile=self.cert_file, ssl_version=PROTOCOL)
573
+ self.sock = ssl.wrap_socket(sock, keyfile=self._client_key, certfile=self._client_cert, ssl_version=PROTOCOL)
571
574
 
572
575
  class CustomHTTPSHandler(urllib_request.HTTPSHandler): # type: ignore[no-redef]
573
576
 
@@ -602,10 +605,6 @@ if hasattr(httplib, 'HTTPSConnection') and hasattr(urllib_request, 'HTTPSHandler
602
605
  return self.do_open(self._build_https_connection, req)
603
606
 
604
607
  def _build_https_connection(self, host, **kwargs):
605
- kwargs.update({
606
- 'cert_file': self.client_cert,
607
- 'key_file': self.client_key,
608
- })
609
608
  try:
610
609
  kwargs['context'] = self._context
611
610
  except AttributeError:
@@ -613,7 +612,7 @@ if hasattr(httplib, 'HTTPSConnection') and hasattr(urllib_request, 'HTTPSHandler
613
612
  if self._unix_socket:
614
613
  return UnixHTTPSConnection(self._unix_socket)(host, **kwargs)
615
614
  if not HAS_SSLCONTEXT:
616
- return CustomHTTPSConnection(host, **kwargs)
615
+ return CustomHTTPSConnection(host, client_cert=self.client_cert, client_key=self.client_key, **kwargs)
617
616
  return httplib.HTTPSConnection(host, **kwargs)
618
617
 
619
618
  @contextmanager
@@ -979,7 +978,7 @@ def atexit_remove_file(filename):
979
978
  pass
980
979
 
981
980
 
982
- def make_context(cafile=None, cadata=None, ciphers=None, validate_certs=True):
981
+ def make_context(cafile=None, cadata=None, ciphers=None, validate_certs=True, client_cert=None, client_key=None):
983
982
  if ciphers is None:
984
983
  ciphers = []
985
984
 
@@ -1006,6 +1005,9 @@ def make_context(cafile=None, cadata=None, ciphers=None, validate_certs=True):
1006
1005
  if ciphers:
1007
1006
  context.set_ciphers(':'.join(map(to_native, ciphers)))
1008
1007
 
1008
+ if client_cert:
1009
+ context.load_cert_chain(client_cert, keyfile=client_key)
1010
+
1009
1011
  return context
1010
1012
 
1011
1013
 
@@ -1514,6 +1516,8 @@ class Request:
1514
1516
  cadata=cadata,
1515
1517
  ciphers=ciphers,
1516
1518
  validate_certs=validate_certs,
1519
+ client_cert=client_cert,
1520
+ client_key=client_key,
1517
1521
  )
1518
1522
  handlers.append(HTTPSClientAuthHandler(client_cert=client_cert,
1519
1523
  client_key=client_key,
ansible/modules/getent.py CHANGED
@@ -12,7 +12,7 @@ DOCUMENTATION = r'''
12
12
  module: getent
13
13
  short_description: A wrapper to the unix getent utility
14
14
  description:
15
- - Runs getent against one of it's various databases and returns information into
15
+ - Runs getent against one of its various databases and returns information into
16
16
  the host's facts, in a getent_<database> prefixed variable.
17
17
  version_added: "1.8"
18
18
  options:
ansible/modules/git.py CHANGED
@@ -45,7 +45,7 @@ options:
45
45
  description:
46
46
  - Will ensure or not that "-o StrictHostKeyChecking=no" is present as an ssh option.
47
47
  - Be aware that this disables a protection against MITM attacks.
48
- - Those using OpenSSH >= 7.5 might want to set I(ssh_opt) to 'StrictHostKeyChecking=accept-new'
48
+ - Those using OpenSSH >= 7.5 might want to set I(ssh_opts) to 'StrictHostKeyChecking=accept-new'
49
49
  instead, it does not remove the MITM issue but it does restrict it to the first attempt.
50
50
  type: bool
51
51
  default: 'no'
ansible/modules/setup.py CHANGED
@@ -114,7 +114,7 @@ author:
114
114
  '''
115
115
 
116
116
  EXAMPLES = r"""
117
- # Display facts from all hosts and store them indexed by I(hostname) at C(/tmp/facts).
117
+ # Display facts from all hosts and store them indexed by `hostname` at `/tmp/facts`.
118
118
  # ansible all -m ansible.builtin.setup --tree /tmp/facts
119
119
 
120
120
  # Display only facts regarding memory found by ansible on all hosts and output them.
ansible/modules/stat.py CHANGED
@@ -47,7 +47,7 @@ options:
47
47
  description:
48
48
  - Use file magic and return data about the nature of the file. this uses
49
49
  the 'file' utility found on most Linux/Unix systems.
50
- - This will add both C(mime_type) and C(charset) fields to the return, if possible.
50
+ - This will add both C(mimetype) and C(charset) fields to the return, if possible.
51
51
  - In Ansible 2.3 this option changed from I(mime) to I(get_mime) and the default changed to C(true).
52
52
  type: bool
53
53
  default: yes
@@ -333,14 +333,14 @@ stat:
333
333
  mimetype:
334
334
  description: file magic data or mime-type
335
335
  returned: success, path exists and user can read stats and
336
- installed python supports it and the I(mime) option was true, will
336
+ installed python supports it and the I(get_mime) option was true, will
337
337
  return C(unknown) on error.
338
338
  type: str
339
339
  sample: application/pdf; charset=binary
340
340
  charset:
341
341
  description: file character set or encoding
342
342
  returned: success, path exists and user can read stats and
343
- installed python supports it and the I(mime) option was true, will
343
+ installed python supports it and the I(get_mime) option was true, will
344
344
  return C(unknown) on error.
345
345
  type: str
346
346
  sample: us-ascii
@@ -77,7 +77,7 @@ EXAMPLES = r'''
77
77
  - ansible.builtin.validate_argument_spec:
78
78
  argument_spec: "{{lookup('ansible.builtin.file', 'nakedoptions.yml'}}"
79
79
  provided_arguments:
80
- but: "that i can define on the include itself, like in it's C(vars:) keyword"
80
+ but: "that i can define on the include itself, like in it's `vars:` keyword"
81
81
 
82
82
  - name: the include itself
83
83
  vars:
ansible/modules/yum.py CHANGED
@@ -23,7 +23,7 @@ options:
23
23
  description:
24
24
  - This module supports C(yum) (as it always has), this is known as C(yum3)/C(YUM3)/C(yum-deprecated) by
25
25
  upstream yum developers. As of Ansible 2.7+, this module also supports C(YUM4), which is the
26
- "new yum" and it has an C(dnf) backend. As of ansible-core 2.15+, C(dnf) will auto select the backend
26
+ "new yum" and it has an C(dnf) backend. As of ansible-core 2.15+, this module will auto select the backend
27
27
  based on the C(ansible_pkg_mgr) fact.
28
28
  - By default, this module will select the backend based on the C(ansible_pkg_mgr) fact.
29
29
  default: "auto"
@@ -735,7 +735,7 @@ class ActionBase(ABC):
735
735
  return remote_paths
736
736
 
737
737
  # we'll need this down here
738
- become_link = get_versioned_doclink('user_guide/become.html')
738
+ become_link = get_versioned_doclink('playbook_guide/playbooks_privilege_escalation.html#risks-of-becoming-an-unprivileged-user')
739
739
 
740
740
  # Step 3f: Common group
741
741
  # Otherwise, we're a normal user. We failed to chown the paths to the
@@ -1,6 +1,6 @@
1
1
  DOCUMENTATION:
2
2
  name: split
3
- version_added: "historical"
3
+ version_added: 2.11
4
4
  short_description: split a string into a list
5
5
  description:
6
6
  - Using Python's text object method C(split) we turn strings into lists via a 'splitting character'.
ansible/release.py CHANGED
@@ -19,6 +19,6 @@
19
19
  from __future__ import (absolute_import, division, print_function)
20
20
  __metaclass__ = type
21
21
 
22
- __version__ = '2.15.2'
22
+ __version__ = '2.15.3'
23
23
  __author__ = 'Ansible, Inc.'
24
24
  __codename__ = "Ten Years Gone"
ansible/utils/encrypt.py CHANGED
@@ -128,7 +128,10 @@ class CryptHash(BaseHash):
128
128
  return ret
129
129
 
130
130
  def _rounds(self, rounds):
131
- if rounds == self.algo_data.implicit_rounds:
131
+ if self.algorithm == 'bcrypt':
132
+ # crypt requires 2 digits for rounds
133
+ return rounds or self.algo_data.implicit_rounds
134
+ elif rounds == self.algo_data.implicit_rounds:
132
135
  # Passlib does not include the rounds if it is the same as implicit_rounds.
133
136
  # Make crypt lib behave the same, by not explicitly specifying the rounds in that case.
134
137
  return None
@@ -148,7 +151,10 @@ class CryptHash(BaseHash):
148
151
  saltstring = "$%s" % ident
149
152
 
150
153
  if rounds:
151
- saltstring += "$rounds=%d" % rounds
154
+ if self.algorithm == 'bcrypt':
155
+ saltstring += "$%d" % rounds
156
+ else:
157
+ saltstring += "$rounds=%d" % rounds
152
158
 
153
159
  saltstring += "$%s" % salt
154
160
 
@@ -178,6 +184,7 @@ class PasslibHash(BaseHash):
178
184
 
179
185
  if not PASSLIB_AVAILABLE:
180
186
  raise AnsibleError("passlib must be installed and usable to hash with '%s'" % algorithm, orig_exc=PASSLIB_E)
187
+ display.vv("Using passlib to hash input with '%s'" % algorithm)
181
188
 
182
189
  try:
183
190
  self.crypt_algo = getattr(passlib.hash, algorithm)
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.1
2
+ Name: ansible-core
3
+ Version: 2.15.3
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: Azure Pipelines, https://dev.azure.com/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-core/
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
+ Classifier: Development Status :: 5 - Production/Stable
16
+ Classifier: Environment :: Console
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Intended Audience :: Information Technology
19
+ Classifier: Intended Audience :: System Administrators
20
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
21
+ Classifier: Natural Language :: English
22
+ Classifier: Operating System :: POSIX
23
+ Classifier: Programming Language :: Python :: 3
24
+ Classifier: Programming Language :: Python :: 3.9
25
+ Classifier: Programming Language :: Python :: 3.10
26
+ Classifier: Programming Language :: Python :: 3.11
27
+ Classifier: Programming Language :: Python :: 3 :: Only
28
+ Classifier: Topic :: System :: Installation/Setup
29
+ Classifier: Topic :: System :: Systems Administration
30
+ Classifier: Topic :: Utilities
31
+ Requires-Python: >=3.9
32
+ Description-Content-Type: text/markdown
33
+ License-File: COPYING
34
+ Requires-Dist: jinja2 >=3.0.0
35
+ Requires-Dist: PyYAML >=5.1
36
+ Requires-Dist: cryptography
37
+ Requires-Dist: packaging
38
+ Requires-Dist: resolvelib <1.1.0,>=0.5.3
39
+ Requires-Dist: importlib-resources <5.1,>=5.0 ; python_version < "3.10"
40
+
41
+ [![PyPI version](https://img.shields.io/pypi/v/ansible-core.svg)](https://pypi.org/project/ansible-core)
42
+ [![Docs badge](https://img.shields.io/badge/docs-latest-brightgreen.svg)](https://docs.ansible.com/ansible/latest/)
43
+ [![Chat badge](https://img.shields.io/badge/chat-IRC-brightgreen.svg)](https://docs.ansible.com/ansible/latest/community/communication.html)
44
+ [![Build Status](https://dev.azure.com/ansible/ansible/_apis/build/status/CI?branchName=devel)](https://dev.azure.com/ansible/ansible/_build/latest?definitionId=20&branchName=devel)
45
+ [![Ansible Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-Ansible-silver.svg)](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html)
46
+ [![Ansible mailing lists](https://img.shields.io/badge/mailing%20lists-Ansible-orange.svg)](https://docs.ansible.com/ansible/latest/community/communication.html#mailing-list-information)
47
+ [![Repository License](https://img.shields.io/badge/license-GPL%20v3.0-brightgreen.svg)](COPYING)
48
+ [![Ansible CII Best Practices certification](https://bestpractices.coreinfrastructure.org/projects/2372/badge)](https://bestpractices.coreinfrastructure.org/projects/2372)
49
+
50
+ # Ansible
51
+
52
+ Ansible is a radically simple IT automation system. It handles
53
+ configuration management, application deployment, cloud provisioning,
54
+ ad-hoc task execution, network automation, and multi-node orchestration. Ansible makes complex
55
+ changes like zero-downtime rolling updates with load balancers easy. More information on the Ansible [website](https://ansible.com/).
56
+
57
+ ## Design Principles
58
+
59
+ * Have an extremely simple setup process with a minimal learning curve.
60
+ * Manage machines quickly and in parallel.
61
+ * Avoid custom-agents and additional open ports, be agentless by
62
+ leveraging the existing SSH daemon.
63
+ * Describe infrastructure in a language that is both machine and human
64
+ friendly.
65
+ * Focus on security and easy auditability/review/rewriting of content.
66
+ * Manage new remote machines instantly, without bootstrapping any
67
+ software.
68
+ * Allow module development in any dynamic language, not just Python.
69
+ * Be usable as non-root.
70
+ * Be the easiest IT automation system to use, ever.
71
+
72
+ ## Use Ansible
73
+
74
+ You can install a released version of Ansible with `pip` or a package manager. See our
75
+ [installation guide](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html) for details on installing Ansible
76
+ on a variety of platforms.
77
+
78
+ Power users and developers can run the `devel` branch, which has the latest
79
+ features and fixes, directly. Although it is reasonably stable, you are more likely to encounter
80
+ breaking changes when running the `devel` branch. We recommend getting involved
81
+ in the Ansible community if you want to run the `devel` branch.
82
+
83
+ ## Get Involved
84
+
85
+ * Read [Community Information](https://docs.ansible.com/ansible/latest/community) for all
86
+ kinds of ways to contribute to and interact with the project,
87
+ including mailing list information and how to submit bug reports and
88
+ code to Ansible.
89
+ * Join a [Working Group](https://github.com/ansible/community/wiki),
90
+ an organized community devoted to a specific technology domain or platform.
91
+ * Submit a proposed code update through a pull request to the `devel` branch.
92
+ * Talk to us before making larger changes
93
+ to avoid duplicate efforts. This not only helps everyone
94
+ know what is going on, but it also helps save time and effort if we decide
95
+ some changes are needed.
96
+ * For a list of email lists, IRC channels and Working Groups, see the
97
+ [Communication page](https://docs.ansible.com/ansible/latest/community/communication.html)
98
+
99
+ ## Coding Guidelines
100
+
101
+ We document our Coding Guidelines in the [Developer Guide](https://docs.ansible.com/ansible/devel/dev_guide/). We particularly suggest you review:
102
+
103
+ * [Contributing your module to Ansible](https://docs.ansible.com/ansible/devel/dev_guide/developing_modules_checklist.html)
104
+ * [Conventions, tips, and pitfalls](https://docs.ansible.com/ansible/devel/dev_guide/developing_modules_best_practices.html)
105
+
106
+ ## Branch Info
107
+
108
+ * The `devel` branch corresponds to the release actively under development.
109
+ * The `stable-2.X` branches correspond to stable releases.
110
+ * 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.
111
+ * See the [Ansible release and maintenance](https://docs.ansible.com/ansible/devel/reference_appendices/release_and_maintenance.html) page for information about active branches.
112
+
113
+ ## Roadmap
114
+
115
+ Based on team and community feedback, an initial roadmap will be published for a major or minor version (ex: 2.7, 2.8).
116
+ The [Ansible Roadmap page](https://docs.ansible.com/ansible/devel/roadmap/) details what is planned and how to influence the roadmap.
117
+
118
+ ## Authors
119
+
120
+ Ansible was created by [Michael DeHaan](https://github.com/mpdehaan)
121
+ and has contributions from over 5000 users (and growing). Thanks everyone!
122
+
123
+ [Ansible](https://www.ansible.com) is sponsored by [Red Hat, Inc.](https://www.redhat.com)
124
+
125
+ ## License
126
+
127
+ GNU General Public License v3.0 or later
128
+
129
+ See [COPYING](COPYING) to see the full text.