This is an exceedingly rough sketch of what attributes might look like - metaclass implementations NOT complete.

pull/9248/merge
Michael DeHaan 10 years ago
parent 0c40c6c23c
commit 335221d79e

@ -94,7 +94,7 @@ tests:
PYTHONPATH=./lib $(NOSETESTS) -d -w test/units -v PYTHONPATH=./lib $(NOSETESTS) -d -w test/units -v
newtests: newtests:
PYTHONPATH=./v2/ $(NOSETESTS) -d -w v2/tests -v PYTHONPATH=./v2 $(NOSETESTS) -d -w test/v2 -v
authors: authors:

@ -1 +1 @@
Subproject commit cb69744bcee4b4217d83b4a30006635ba69e2aa0 Subproject commit f624689bad24cb3a7b2ef43d5280b5f4fbabb5bd

@ -0,0 +1,2 @@
# TODO: header

@ -0,0 +1,37 @@
# TODO: header
from ansible.playbook.task import Task
import unittest
basic_shell_task = dict(
name = 'Test Task',
shell = 'echo hi'
)
class TestTask(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_can_construct_empty_task():
t = Task()
def test_can_construct_task_with_role():
pass
def test_can_construct_task_with_block():
pass
def test_can_construct_task_with_role_and_block():
pass
def test_can_load_simple_task():
t = Task.load(basic_shell_task)
assert t.name == basic_shell_task['name']
assert t.module == 'shell'
assert t.args == 'echo hi'

@ -15,8 +15,6 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import ansible.utils
class Playbook(object): class Playbook(object):
def __init__(self, filename): def __init__(self, filename):
self.ds = v2.utils.load_yaml_from_file(filename) self.ds = v2.utils.load_yaml_from_file(filename)

@ -15,11 +15,23 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
from errors import AnsibleError #from ansible.cmmon.errors import AnsibleError
from playbook.tag import Tag #from playbook.tag import Tag
class Base(object): class Base(object):
def __init__(self): def __init__(self, attribute):
pass pass
def add_attribute(self):
self.attributes.push(attribute)
def load(self, data):
for attribute in self.attributes:
attribute.load(data)
def validate(self):
for attribute in self.attributes:
attribute.validate(self)

@ -15,10 +15,11 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
from playbook.base import Base from ansible.playbook.base import Base
from playbook.conditional import Conditional from ansible.playbook.attribute import Attribute, FieldAttribute
from errors import AnsibleError from ansible.playbook.conditional import Conditional
from ansible import utils #from ansible.common.errors import AnsibleError
#from ansible import utils
# TODO: it would be fantastic (if possible) if a task new where in the YAML it was defined for describing # TODO: it would be fantastic (if possible) if a task new where in the YAML it was defined for describing
# it in error conditions # it in error conditions
@ -36,39 +37,58 @@ class Task(Base):
""" """
# ================================================================================= # =================================================================================
# KEYS AND SLOTS: defines what variables in are valid in the data structure and # ATTRIBUTES
# the object itself # load_<attribute_name> and
# validate_<attribute_name>
VALID_KEYS = [ # will be used if defined
'always_run', 'any_errors_fatal', 'async', 'connection', 'delay', 'delegate_to', 'environment', # might be possible to define others
'first_available_file', 'ignore_errors', 'include', 'local_action', 'meta', 'name', 'no_log',
'notify', 'poll', 'register', 'remote_user', 'retries', 'run_once', 'su', 'su_pass', 'su_user', always_run = FieldAttribute(isa='bool')
'sudo', 'sudo_pass', 'sudo_user', 'transport', 'until' any_errors_fatal = FieldAttribute(isa='bool')
] async = FieldAttribute(isa='int')
connection = FieldAttribute(isa='string')
__slots__ = [ delay = FieldAttribute(isa='int')
'_always_run', '_any_errors_fatal', '_async', '_connection', '_delay', '_delegate_to', '_environment', delegate_to = FieldAttribute(isa='string')
'_first_available_file', '_ignore_errors', '_include', '_local_action', '_meta', '_name', '_no_log', environment = FieldAttribute(isa='dict')
'_notify', '_poll', '_register', '_remote_user', '_retries', '_run_once', '_su', '_su_pass', '_su_user', first_available_file = FieldAttribute(isa='list')
'_sudo', '_sudo_pass', '_sudo_user', '_transport', '_until' ignore_errors = FieldAttribute(isa='bool')
]
# FIXME: this should not be a Task
# include = FieldAttribute(isa='string')
local_action = FieldAttribute(isa='string', alias='action', post_validate='_set_local_action')
# FIXME: this should not be a Task
meta = FieldAttribute(isa='string')
name = FieldAttribute(isa='string', post_validate='_set_name')
no_log = FieldAttribute(isa='bool')
notify = FieldAttribute(isa='list')
poll = FieldAttribute(isa='integer')
register = FieldAttribute(isa='string')
remote_user = FieldAttribute(isa='string')
retries = FieldAttribute(isa='integer')
run_once = FieldAttribute(isa='bool')
su = FieldAttribute(isa='bool')
su_pass = FieldAttribute(isa='string')
su_user = FieldAttribute(isa='string')
sudo = FieldAttribute(isa='bool')
sudo_user = FieldAttribute(isa='string')
sudo_pass = FieldAttribute(isa='string')
transport = FieldAttribute(isa='string')
until = FieldAttribute(isa='list') # ?
role = Attribute()
block = Attribute()
# ================================================================================== # ==================================================================================
def __init__(self, block=None, role=None): def __init__(self, block=None, role=None):
''' constructors a task, without the Task.load classmethod, it will be pretty blank ''' ''' constructors a task, without the Task.load classmethod, it will be pretty blank '''
self._block = block self.block = block
self._role = role self.role = role
self._reset()
super(Task, self).__init__() super(Task, self).__init__()
# TODO: move to BaseObject
def _reset(self):
''' clear out the object '''
for x in __slots__:
setattr(x, None)
# ================================================================================== # ==================================================================================
# BASIC ACCESSORS # BASIC ACCESSORS
@ -83,34 +103,28 @@ class Task(Base):
''' returns a human readable representation of the task ''' ''' returns a human readable representation of the task '''
return "TASK: %s" % self.get_name() return "TASK: %s" % self.get_name()
# FIXME: does a task have variables? @classmethod
def get_vars(self): def load(self, block=None, role=None, data=None):
''' return the variables associated with the task ''' self = Task(block=block, role=role)
raise exception.NotImplementedError() self._load_field_attributes(data) # from BaseObject
self._load_plugin_attributes(data) # from here, becuase of lookupPlugins
def get_role(self): return self
''' return the role associated with the task '''
return self._role
def get_block(self): def _load_plugin_attributes(self, data):
''' return the block the task is in ''' module_names = self._module_names()
return self._block for (k,v) in data.iteritems():
if k in module_names:
self.module = k
self.args = v
# ================================================================================== # ==================================================================================
# LOAD: functions related to walking the datastructure and storing data # BELOW THIS LINE
# info below this line is "old" and is before the attempt to build Attributes
def _load_parameters(data): # use as reference but plan to replace and radically simplify
''' validate/transmogrify/assign any module parameters for this task ''' # ==================================================================================
if isinstance(data, dict): LEGACY = """
return dict(_parameters=data)
elif isinstance(data, basestring):
return dict(_parameters=utils.parse_kv(data))
elif isinstance(data, None):
return dict(_parameters='')
else:
raise AnsibleError("invalid arguments specified, got '%s' (type=%s')" % (data, type(data)))
def _load_action(self, ds, k, v): def _load_action(self, ds, k, v):
''' validate/transmogrify/assign the module and parameters if used in 'action/local_action' format ''' ''' validate/transmogrify/assign the module and parameters if used in 'action/local_action' format '''
@ -301,3 +315,5 @@ class Task(Base):
if self._first_available_file and self._lookup_plugin: if self._first_available_file and self._lookup_plugin:
raise AnsibleError("with_(plugin), and first_available_file are mutually incompatible in a single task") raise AnsibleError("with_(plugin), and first_available_file are mutually incompatible in a single task")
"""

@ -15,8 +15,8 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
from v2.inventory import Host #from v2.inventory import Host
from v2.playbook import Task #from v2.playbook import Task
class Runner(object): class Runner(object):
pass pass

Loading…
Cancel
Save