/lib/ansible/playbook/__init__.py

https://github.com/debfx/ansible · Python · 116 lines · 67 code · 22 blank · 27 comment · 17 complexity · bdcdb3f39a4e8a071b2def6d8d91f16b MD5 · raw file

  1. # (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
  2. #
  3. # This file is part of Ansible
  4. #
  5. # Ansible is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # Ansible is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
  17. # Make coding more python3-ish
  18. from __future__ import (absolute_import, division, print_function)
  19. __metaclass__ = type
  20. import os
  21. from ansible import constants as C
  22. from ansible.errors import AnsibleParserError
  23. from ansible.module_utils._text import to_bytes, to_text, to_native
  24. from ansible.playbook.play import Play
  25. from ansible.playbook.playbook_include import PlaybookInclude
  26. from ansible.plugins.loader import get_all_plugin_loaders
  27. from ansible.utils.display import Display
  28. display = Display()
  29. __all__ = ['Playbook']
  30. class Playbook:
  31. def __init__(self, loader):
  32. # Entries in the datastructure of a playbook may
  33. # be either a play or an include statement
  34. self._entries = []
  35. self._basedir = to_text(os.getcwd(), errors='surrogate_or_strict')
  36. self._loader = loader
  37. self._file_name = None
  38. @staticmethod
  39. def load(file_name, variable_manager=None, loader=None):
  40. pb = Playbook(loader=loader)
  41. pb._load_playbook_data(file_name=file_name, variable_manager=variable_manager)
  42. return pb
  43. def _load_playbook_data(self, file_name, variable_manager, vars=None):
  44. if os.path.isabs(file_name):
  45. self._basedir = os.path.dirname(file_name)
  46. else:
  47. self._basedir = os.path.normpath(os.path.join(self._basedir, os.path.dirname(file_name)))
  48. # set the loaders basedir
  49. cur_basedir = self._loader.get_basedir()
  50. self._loader.set_basedir(self._basedir)
  51. self._file_name = file_name
  52. # dynamically load any plugins from the playbook directory
  53. for name, obj in get_all_plugin_loaders():
  54. if obj.subdir:
  55. plugin_path = os.path.join(self._basedir, obj.subdir)
  56. if os.path.isdir(to_bytes(plugin_path)):
  57. obj.add_directory(plugin_path)
  58. try:
  59. ds = self._loader.load_from_file(os.path.basename(file_name))
  60. except UnicodeDecodeError as e:
  61. raise AnsibleParserError("Could not read playbook (%s) due to encoding issues: %s" % (file_name, to_native(e)))
  62. # check for errors and restore the basedir in case this error is caught and handled
  63. if not ds:
  64. self._loader.set_basedir(cur_basedir)
  65. raise AnsibleParserError("Empty playbook, nothing to do", obj=ds)
  66. elif not isinstance(ds, list):
  67. self._loader.set_basedir(cur_basedir)
  68. raise AnsibleParserError("A playbook must be a list of plays, got a %s instead" % type(ds), obj=ds)
  69. # Parse the playbook entries. For plays, we simply parse them
  70. # using the Play() object, and includes are parsed using the
  71. # PlaybookInclude() object
  72. for entry in ds:
  73. if not isinstance(entry, dict):
  74. # restore the basedir in case this error is caught and handled
  75. self._loader.set_basedir(cur_basedir)
  76. raise AnsibleParserError("playbook entries must be either a valid play or an include statement", obj=entry)
  77. if any(action in entry for action in ('import_playbook', 'include')):
  78. if 'include' in entry:
  79. display.deprecated("'include' for playbook includes. You should use 'import_playbook' instead", version="2.12")
  80. pb = PlaybookInclude.load(entry, basedir=self._basedir, variable_manager=variable_manager, loader=self._loader)
  81. if pb is not None:
  82. self._entries.extend(pb._entries)
  83. else:
  84. which = entry.get('import_playbook', entry.get('include', entry))
  85. display.display("skipping playbook '%s' due to conditional test failure" % which, color=C.COLOR_SKIP)
  86. else:
  87. entry_obj = Play.load(entry, variable_manager=variable_manager, loader=self._loader, vars=vars)
  88. self._entries.append(entry_obj)
  89. # we're done, so restore the old basedir in the loader
  90. self._loader.set_basedir(cur_basedir)
  91. def get_loader(self):
  92. return self._loader
  93. def get_plays(self):
  94. return self._entries[:]