Adding Play class for v2

This commit is contained in:
James Cammarata 2014-11-04 15:16:11 -06:00
parent afd8cca345
commit 0ed9746db3
8 changed files with 349 additions and 55 deletions

View file

@ -190,7 +190,7 @@ class ModuleArgsParser:
task, dealing with all sorts of levels of fuzziness. task, dealing with all sorts of levels of fuzziness.
''' '''
assert type(ds) == dict assert isinstance(ds, dict)
thing = None thing = None

View file

@ -19,9 +19,9 @@
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
from ansible.playbook.base import Base
from ansible.playbook.task import Task
from ansible.playbook.attribute import Attribute, FieldAttribute from ansible.playbook.attribute import Attribute, FieldAttribute
from ansible.playbook.base import Base
from ansible.playbook.helpers import load_list_of_tasks
class Block(Base): class Block(Base):
@ -60,25 +60,20 @@ class Block(Base):
is_block = True is_block = True
break break
if not is_block: if not is_block:
return dict(block=ds) if isinstance(ds, list):
return dict(block=ds)
else:
return dict(block=[ds])
return ds return ds
def _load_list_of_tasks(self, ds):
assert type(ds) == list
task_list = []
for task in ds:
t = Task.load(task)
task_list.append(t)
return task_list
def _load_block(self, attr, ds): def _load_block(self, attr, ds):
return self._load_list_of_tasks(ds) return load_list_of_tasks(ds)
def _load_rescue(self, attr, ds): def _load_rescue(self, attr, ds):
return self._load_list_of_tasks(ds) return load_list_of_tasks(ds)
def _load_always(self, attr, ds): def _load_always(self, attr, ds):
return self._load_list_of_tasks(ds) return load_list_of_tasks(ds)
# not currently used # not currently used
#def _load_otherwise(self, attr, ds): #def _load_otherwise(self, attr, ds):

View file

@ -0,0 +1,76 @@
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
from types import NoneType
def load_list_of_blocks(ds, role=None, loader=None):
'''
Given a list of mixed task/block data (parsed from YAML),
return a list of Block() objects, where implicit blocks
are created for each bare Task.
'''
# we import here to prevent a circular dependency with imports
from ansible.playbook.block import Block
assert type(ds) in (list, NoneType)
block_list = []
if ds:
for block in ds:
b = Block.load(block, role=role, loader=loader)
block_list.append(b)
return block_list
def load_list_of_tasks(ds, block=None, role=None, loader=None):
'''
Given a list of task datastructures (parsed from YAML),
return a list of Task() objects.
'''
# we import here to prevent a circular dependency with imports
from ansible.playbook.task import Task
assert type(ds) == list
task_list = []
for task in ds:
t = Task.load(task, block=block, role=role, loader=loader)
task_list.append(t)
return task_list
def load_list_of_roles(ds, loader=None):
'''
Loads and returns a list of RoleInclude objects from the datastructure
list of role definitions
'''
# we import here to prevent a circular dependency with imports
from ansible.playbook.role.include import RoleInclude
assert isinstance(ds, list)
roles = []
for role_def in ds:
i = RoleInclude.load(role_def, loader=loader)
roles.append(i)
return roles

View file

@ -18,3 +18,140 @@
# Make coding more python3-ish # Make coding more python3-ish
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
from ansible.errors import AnsibleError, AnsibleParserError
from ansible.parsing.yaml import DataLoader
from ansible.playbook.attribute import Attribute, FieldAttribute
from ansible.playbook.base import Base
from ansible.playbook.helpers import load_list_of_blocks, load_list_of_roles
__all__ = ['Play']
class Play(Base):
"""
A play is a language feature that represents a list of roles and/or
task/handler blocks to execute on a given set of hosts.
Usage:
Play.load(datastructure) -> Play
Play.something(...)
"""
# =================================================================================
# Connection-Related Attributes
_accelerate = FieldAttribute(isa='bool', default=False)
_accelerate_ipv6 = FieldAttribute(isa='bool', default=False)
_accelerate_port = FieldAttribute(isa='int', default=5099)
_connection = FieldAttribute(isa='string', default='smart')
_gather_facts = FieldAttribute(isa='string', default='smart')
_hosts = FieldAttribute(isa='list', default=[])
_name = FieldAttribute(isa='string', default='<no name specified>')
_port = FieldAttribute(isa='int', default=22)
_remote_user = FieldAttribute(isa='string', default='root')
_su = FieldAttribute(isa='bool', default=False)
_su_user = FieldAttribute(isa='string', default='root')
_sudo = FieldAttribute(isa='bool', default=False)
_sudo_user = FieldAttribute(isa='string', default='root')
_tags = FieldAttribute(isa='list', default=[])
# Variable Attributes
_vars = FieldAttribute(isa='dict', default=dict())
_vars_files = FieldAttribute(isa='list', default=[])
_vars_prompt = FieldAttribute(isa='dict', default=dict())
_vault_password = FieldAttribute(isa='string')
# Block (Task) Lists Attributes
_handlers = FieldAttribute(isa='list', default=[])
_pre_tasks = FieldAttribute(isa='list', default=[])
_post_tasks = FieldAttribute(isa='list', default=[])
_tasks = FieldAttribute(isa='list', default=[])
# Role Attributes
_roles = FieldAttribute(isa='list', default=[])
# Flag/Setting Attributes
_any_errors_fatal = FieldAttribute(isa='bool', default=False)
_max_fail_percentage = FieldAttribute(isa='string', default='0')
_no_log = FieldAttribute(isa='bool', default=False)
_serial = FieldAttribute(isa='int', default=0)
# =================================================================================
def __init__(self):
super(Play, self).__init__()
def __repr__(self):
return self.get_name()
def get_name(self):
''' return the name of the Play '''
return "PLAY: %s" % self._attributes.get('name')
@staticmethod
def load(data, loader=None):
p = Play()
return p.load_data(data, loader=loader)
def munge(self, ds):
'''
Adjusts play datastructure to cleanup old/legacy items
'''
assert isinstance(ds, dict)
# The use of 'user' in the Play datastructure was deprecated to
# line up with the same change for Tasks, due to the fact that
# 'user' conflicted with the user module.
if 'user' in ds:
# this should never happen, but error out with a helpful message
# to the user if it does...
if 'remote_user' in ds:
raise AnsibleParserError("both 'user' and 'remote_user' are set for %s. The use of 'user' is deprecated, and should be removed" % self.get_name(), obj=ds)
ds['remote_user'] = ds['user']
del ds['user']
return ds
def _load_tasks(self, attr, ds):
'''
Loads a list of blocks from a list which may be mixed tasks/blocks.
Bare tasks outside of a block are given an implicit block.
'''
return load_list_of_blocks(ds)
def _load_pre_tasks(self, attr, ds):
'''
Loads a list of blocks from a list which may be mixed tasks/blocks.
Bare tasks outside of a block are given an implicit block.
'''
return load_list_of_blocks(ds)
def _load_post_tasks(self, attr, ds):
'''
Loads a list of blocks from a list which may be mixed tasks/blocks.
Bare tasks outside of a block are given an implicit block.
'''
return load_list_of_blocks(ds)
def _load_handlers(self, attr, ds):
'''
Loads a list of blocks from a list which may be mixed handlers/blocks.
Bare handlers outside of a block are given an implicit block.
'''
return load_list_of_blocks(ds)
def _load_roles(self, attr, ds):
'''
Loads and returns a list of RoleInclude objects from the datastructure
list of role definitions
'''
return load_list_of_roles(ds, loader=self._loader)
# FIXME: post_validation needs to ensure that su/sudo are not both set

View file

@ -30,7 +30,7 @@ from ansible.errors import AnsibleError, AnsibleParserError
from ansible.parsing.yaml import DataLoader from ansible.parsing.yaml import DataLoader
from ansible.playbook.attribute import FieldAttribute from ansible.playbook.attribute import FieldAttribute
from ansible.playbook.base import Base from ansible.playbook.base import Base
from ansible.playbook.block import Block from ansible.playbook.helpers import load_list_of_blocks
from ansible.playbook.role.include import RoleInclude from ansible.playbook.role.include import RoleInclude
from ansible.playbook.role.metadata import RoleMetadata from ansible.playbook.role.metadata import RoleMetadata
@ -95,11 +95,11 @@ class Role:
task_data = self._load_role_yaml('tasks') task_data = self._load_role_yaml('tasks')
if task_data: if task_data:
self._task_blocks = self._load_list_of_blocks(task_data) self._task_blocks = load_list_of_blocks(task_data)
handler_data = self._load_role_yaml('handlers') handler_data = self._load_role_yaml('handlers')
if handler_data: if handler_data:
self._handler_blocks = self._load_list_of_blocks(handler_data) self._handler_blocks = load_list_of_blocks(handler_data)
# vars and default vars are regular dictionaries # vars and default vars are regular dictionaries
self._role_vars = self._load_role_yaml('vars') self._role_vars = self._load_role_yaml('vars')
@ -135,23 +135,6 @@ class Role:
return m # exactly one main file return m # exactly one main file
return possible_mains[0] # zero mains (we still need to return something) return possible_mains[0] # zero mains (we still need to return something)
def _load_list_of_blocks(self, ds):
'''
Given a list of mixed task/block data (parsed from YAML),
return a list of Block() objects, where implicit blocks
are created for each bare Task.
'''
assert type(ds) in (list, NoneType)
block_list = []
if ds:
for block in ds:
b = Block(block)
block_list.append(b)
return block_list
def _load_dependencies(self): def _load_dependencies(self):
''' '''
Recursively loads role dependencies from the metadata list of Recursively loads role dependencies from the metadata list of

View file

@ -24,6 +24,7 @@ from six import iteritems, string_types
from ansible.errors import AnsibleParserError from ansible.errors import AnsibleParserError
from ansible.playbook.attribute import Attribute, FieldAttribute from ansible.playbook.attribute import Attribute, FieldAttribute
from ansible.playbook.base import Base from ansible.playbook.base import Base
from ansible.playbook.helpers import load_list_of_roles
from ansible.playbook.role.include import RoleInclude from ansible.playbook.role.include import RoleInclude
@ -58,18 +59,10 @@ class RoleMetadata(Base):
def _load_dependencies(self, attr, ds): def _load_dependencies(self, attr, ds):
''' '''
This is a helper loading function for the dependencis list, This is a helper loading function for the dependencies list,
which returns a list of RoleInclude objects which returns a list of RoleInclude objects
''' '''
return load_list_of_roles(ds, loader=self._loader)
assert isinstance(ds, list)
deps = []
for role_def in ds:
i = RoleInclude.load(role_def, loader=self._loader)
deps.append(i)
return deps
def _load_galaxy_info(self, attr, ds): def _load_galaxy_info(self, attr, ds):
''' '''

View file

@ -37,16 +37,6 @@ class TestBlock(unittest.TestCase):
def test_construct_block_with_role(self): def test_construct_block_with_role(self):
pass pass
def test_block__load_list_of_tasks(self):
task = dict(action='test')
b = Block()
self.assertEqual(b._load_list_of_tasks([]), [])
res = b._load_list_of_tasks([task])
self.assertEqual(len(res), 1)
assert isinstance(res[0], Task)
res = b._load_list_of_tasks([task,task,task])
self.assertEqual(len(res), 3)
def test_load_block_simple(self): def test_load_block_simple(self):
ds = dict( ds = dict(
block = [], block = [],

View file

@ -0,0 +1,120 @@
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.compat.tests import unittest
from ansible.compat.tests.mock import patch, MagicMock
from ansible.errors import AnsibleError, AnsibleParserError
from ansible.playbook.play import Play
from ansible.playbook.role import Role
from ansible.playbook.task import Task
from test.mock.loader import DictDataLoader
class TestPlay(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_empty_play(self):
p = Play.load(dict())
self.assertEqual(str(p), "PLAY: <no name specified>")
def test_basic_play(self):
p = Play.load(dict(
name="test play",
hosts=['foo'],
gather_facts=False,
connection='local',
remote_user="root",
sudo=True,
sudo_user="testing",
))
def test_play_with_user_conflict(self):
p = Play.load(dict(
name="test play",
hosts=['foo'],
user="testing",
gather_facts=False,
))
self.assertEqual(p.remote_user, "testing")
def test_play_with_user_conflict(self):
play_data = dict(
name="test play",
hosts=['foo'],
user="testing",
remote_user="testing",
)
self.assertRaises(AnsibleParserError, Play.load, play_data)
def test_play_with_tasks(self):
p = Play.load(dict(
name="test play",
hosts=['foo'],
gather_facts=False,
tasks=[dict(action='shell echo "hello world"')],
))
def test_play_with_handlers(self):
p = Play.load(dict(
name="test play",
hosts=['foo'],
gather_facts=False,
handlers=[dict(action='shell echo "hello world"')],
))
def test_play_with_pre_tasks(self):
p = Play.load(dict(
name="test play",
hosts=['foo'],
gather_facts=False,
pre_tasks=[dict(action='shell echo "hello world"')],
))
def test_play_with_post_tasks(self):
p = Play.load(dict(
name="test play",
hosts=['foo'],
gather_facts=False,
post_tasks=[dict(action='shell echo "hello world"')],
))
def test_play_with_roles(self):
fake_loader = DictDataLoader({
'/etc/ansible/roles/foo/tasks.yml': """
- name: role task
shell: echo "hello world"
""",
})
p = Play.load(dict(
name="test play",
hosts=['foo'],
gather_facts=False,
roles=['foo'],
), loader=fake_loader)