Add Unit/Integration tests for Dryd-33
- Add unit tests for a couple bugs pointed out by Dryd-33 work - Add integration tests for work implemented via Dryd-33 Change-Id: I814ee9631fb488dedf484ff8e9c8db0866c9f280
This commit is contained in:
parent
693d71cd61
commit
3d9724717c
@ -17,9 +17,10 @@ from threading import Thread
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
import drydock_provisioner.objects.fields as hd_fields
|
import drydock_provisioner.objects.fields as hd_fields
|
||||||
import drydock_provisioner.statemgmt as statemgmt
|
|
||||||
import drydock_provisioner.error as errors
|
import drydock_provisioner.error as errors
|
||||||
|
|
||||||
|
from drydock_provisioner.orchestrator.actions.orchestrator import Noop
|
||||||
|
|
||||||
|
|
||||||
class ProviderDriver(object):
|
class ProviderDriver(object):
|
||||||
"""Generic driver for executing driver actions."""
|
"""Generic driver for executing driver actions."""
|
||||||
@ -47,8 +48,9 @@ class ProviderDriver(object):
|
|||||||
task_action = task.action
|
task_action = task.action
|
||||||
|
|
||||||
if task_action in self.supported_actions:
|
if task_action in self.supported_actions:
|
||||||
task_runner = DriverActionRunner(task_id, self.state_manager,
|
# Just use the Noop action
|
||||||
self.orchestrator)
|
task_action = Noop(task, self.orchestrator, self.state_manager)
|
||||||
|
task_runner = DriverActionRunner(task_action)
|
||||||
task_runner.start()
|
task_runner.start()
|
||||||
|
|
||||||
while task_runner.is_alive():
|
while task_runner.is_alive():
|
||||||
@ -62,17 +64,12 @@ class ProviderDriver(object):
|
|||||||
|
|
||||||
# Execute a single task in a separate thread
|
# Execute a single task in a separate thread
|
||||||
class DriverActionRunner(Thread):
|
class DriverActionRunner(Thread):
|
||||||
def __init__(self, action=None, state_manager=None, orchestrator=None):
|
def __init__(self, action=None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.orchestrator = orchestrator
|
|
||||||
|
|
||||||
if isinstance(state_manager, statemgmt.DesignState):
|
|
||||||
self.state_manager = state_manager
|
|
||||||
else:
|
|
||||||
raise errors.DriverError("Invalid state manager specified")
|
|
||||||
|
|
||||||
self.action = action
|
self.action = action
|
||||||
|
self.orchestrator = action.orchestrator
|
||||||
|
self.state_manager = action.state_manager
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.run_action()
|
self.run_action()
|
||||||
|
@ -21,7 +21,7 @@ from drydock_provisioner.drivers.driver import ProviderDriver
|
|||||||
class OobDriver(ProviderDriver):
|
class OobDriver(ProviderDriver):
|
||||||
"""Genneric driver for OOB actions."""
|
"""Genneric driver for OOB actions."""
|
||||||
|
|
||||||
oob_types_supported = ['']
|
oob_types_supported = ['ipmi']
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(OobDriver, self).__init__(**kwargs)
|
super(OobDriver, self).__init__(**kwargs)
|
||||||
|
@ -380,7 +380,8 @@ class Task(object):
|
|||||||
'request_context':
|
'request_context':
|
||||||
json.dumps(self.request_context.to_dict())
|
json.dumps(self.request_context.to_dict())
|
||||||
if self.request_context is not None else None,
|
if self.request_context is not None else None,
|
||||||
'node_filter': self.node_filter,
|
'node_filter':
|
||||||
|
self.node_filter,
|
||||||
'action':
|
'action':
|
||||||
self.action,
|
self.action,
|
||||||
'terminated':
|
'terminated':
|
||||||
|
@ -86,7 +86,8 @@ class BaseAction(object):
|
|||||||
self.task.register_subtask(split_task)
|
self.task.register_subtask(split_task)
|
||||||
action = self.__class__(split_task, self.orchestrator,
|
action = self.__class__(split_task, self.orchestrator,
|
||||||
self.state_manager)
|
self.state_manager)
|
||||||
split_tasks[split_task.get_id().bytes] = te.submit(action.start)
|
split_tasks[split_task.get_id().bytes] = te.submit(
|
||||||
|
action.start)
|
||||||
|
|
||||||
return split_tasks
|
return split_tasks
|
||||||
|
|
||||||
@ -185,7 +186,9 @@ class Noop(BaseAction):
|
|||||||
else:
|
else:
|
||||||
self.logger.debug("Marked task as successful.")
|
self.logger.debug("Marked task as successful.")
|
||||||
self.task.set_status(hd_fields.TaskStatus.Complete)
|
self.task.set_status(hd_fields.TaskStatus.Complete)
|
||||||
self.task.success()
|
target_nodes = self.orchestrator.get_target_nodes(self.task)
|
||||||
|
for n in target_nodes:
|
||||||
|
self.task.success(focus=n.name)
|
||||||
self.task.add_status_msg(
|
self.task.add_status_msg(
|
||||||
msg="Noop action.", ctx_type='NA', ctx='NA', error=False)
|
msg="Noop action.", ctx_type='NA', ctx='NA', error=False)
|
||||||
self.task.save()
|
self.task.save()
|
||||||
|
@ -302,7 +302,6 @@ class Orchestrator(object):
|
|||||||
|
|
||||||
def get_target_nodes(self, task, failures=False, successes=False):
|
def get_target_nodes(self, task, failures=False, successes=False):
|
||||||
"""Compute list of target nodes for given ``task``.
|
"""Compute list of target nodes for given ``task``.
|
||||||
|
|
||||||
If failures is true, then create a node_filter based on task result
|
If failures is true, then create a node_filter based on task result
|
||||||
failures. If successes is true, then create a node_filter based on
|
failures. If successes is true, then create a node_filter based on
|
||||||
task result successes. If both are true, raise an exception. If neither
|
task result successes. If both are true, raise an exception. If neither
|
||||||
|
57
tests/integration/postgres/test_action_prepare_nodes.py
Normal file
57
tests/integration/postgres/test_action_prepare_nodes.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
"""Generic testing for the orchestrator."""
|
||||||
|
import drydock_provisioner.orchestrator.orchestrator as orch
|
||||||
|
import drydock_provisioner.objects.fields as hd_fields
|
||||||
|
|
||||||
|
from drydock_provisioner.orchestrator.actions.orchestrator import PrepareNodes
|
||||||
|
|
||||||
|
|
||||||
|
class TestActionPrepareNodes(object):
|
||||||
|
def test_preparenodes(self, input_files, deckhand_ingester, setup,
|
||||||
|
drydock_state):
|
||||||
|
input_file = input_files.join("deckhand_fullsite.yaml")
|
||||||
|
|
||||||
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
|
# Build a dummy object that looks like an oslo_config object
|
||||||
|
# so the orchestrator is configured w/ Noop drivers
|
||||||
|
class DummyConf(object):
|
||||||
|
oob_driver = ['drydock_provisioner.drivers.oob.driver.OobDriver']
|
||||||
|
node_driver = 'drydock_provisioner.drivers.node.driver.NodeDriver'
|
||||||
|
network_driver = None
|
||||||
|
|
||||||
|
orchestrator = orch.Orchestrator(
|
||||||
|
enabled_drivers=DummyConf(),
|
||||||
|
state_manager=drydock_state,
|
||||||
|
ingester=deckhand_ingester)
|
||||||
|
|
||||||
|
task = orchestrator.create_task(
|
||||||
|
design_ref=design_ref,
|
||||||
|
action=hd_fields.OrchestratorAction.PrepareNodes)
|
||||||
|
|
||||||
|
action = PrepareNodes(task, orchestrator, drydock_state)
|
||||||
|
action.start()
|
||||||
|
|
||||||
|
task = drydock_state.get_task(task.get_id())
|
||||||
|
|
||||||
|
assert task.result.status == hd_fields.ActionResult.Success
|
||||||
|
|
||||||
|
# check that the PrepareNodes action was split
|
||||||
|
# with 2 nodes in the definition
|
||||||
|
assert len(task.subtask_id_list) == 2
|
||||||
|
|
||||||
|
for st_id in task.subtask_id_list:
|
||||||
|
st = drydock_state.get_task(st_id)
|
||||||
|
assert st.action == hd_fields.OrchestratorAction.PrepareNodes
|
@ -12,8 +12,6 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
"""Generic testing for the orchestrator."""
|
"""Generic testing for the orchestrator."""
|
||||||
import pytest
|
|
||||||
|
|
||||||
import drydock_provisioner.orchestrator.orchestrator as orch
|
import drydock_provisioner.orchestrator.orchestrator as orch
|
||||||
import drydock_provisioner.objects.fields as hd_fields
|
import drydock_provisioner.objects.fields as hd_fields
|
||||||
|
|
||||||
@ -21,7 +19,6 @@ from drydock_provisioner.orchestrator.actions.orchestrator import PrepareSite
|
|||||||
|
|
||||||
|
|
||||||
class TestActionPrepareSite(object):
|
class TestActionPrepareSite(object):
|
||||||
@pytest.mark.skip(reason="test failure fixed in next PS")
|
|
||||||
def test_preparesite(self, input_files, deckhand_ingester, setup,
|
def test_preparesite(self, input_files, deckhand_ingester, setup,
|
||||||
drydock_state):
|
drydock_state):
|
||||||
input_file = input_files.join("deckhand_fullsite.yaml")
|
input_file = input_files.join("deckhand_fullsite.yaml")
|
||||||
|
@ -18,19 +18,17 @@ import time
|
|||||||
import drydock_provisioner.orchestrator.orchestrator as orch
|
import drydock_provisioner.orchestrator.orchestrator as orch
|
||||||
import drydock_provisioner.objects.fields as hd_fields
|
import drydock_provisioner.objects.fields as hd_fields
|
||||||
|
|
||||||
from drydock_provisioner.ingester.ingester import Ingester
|
|
||||||
|
|
||||||
|
|
||||||
class TestClass(object):
|
class TestClass(object):
|
||||||
def test_task_complete(self, setup, blank_state):
|
def test_task_complete(self, yaml_ingester, input_files, setup, blank_state):
|
||||||
ingester = Ingester()
|
input_file = input_files.join("fullsite.yaml")
|
||||||
ingester.enable_plugin(
|
design_ref = "file://%s" % str(input_file)
|
||||||
'drydock_provisioner.ingester.plugins.yaml.YamlIngester')
|
|
||||||
|
|
||||||
orchestrator = orch.Orchestrator(
|
orchestrator = orch.Orchestrator(
|
||||||
state_manager=blank_state, ingester=ingester)
|
state_manager=blank_state, ingester=yaml_ingester)
|
||||||
orch_task = orchestrator.create_task(
|
orch_task = orchestrator.create_task(
|
||||||
action=hd_fields.OrchestratorAction.Noop)
|
action=hd_fields.OrchestratorAction.Noop,
|
||||||
|
design_ref=design_ref)
|
||||||
orch_task.set_status(hd_fields.TaskStatus.Queued)
|
orch_task.set_status(hd_fields.TaskStatus.Queued)
|
||||||
orch_task.save()
|
orch_task.save()
|
||||||
|
|
||||||
@ -47,15 +45,15 @@ class TestClass(object):
|
|||||||
orchestrator.stop_orchestrator()
|
orchestrator.stop_orchestrator()
|
||||||
orch_thread.join(10)
|
orch_thread.join(10)
|
||||||
|
|
||||||
def test_task_termination(self, setup, blank_state):
|
def test_task_termination(self, input_files, yaml_ingester, setup, blank_state):
|
||||||
ingester = Ingester()
|
input_file = input_files.join("fullsite.yaml")
|
||||||
ingester.enable_plugin(
|
design_ref = "file://%s" % str(input_file)
|
||||||
'drydock_provisioner.ingester.plugins.yaml.YamlIngester')
|
|
||||||
|
|
||||||
orchestrator = orch.Orchestrator(
|
orchestrator = orch.Orchestrator(
|
||||||
state_manager=blank_state, ingester=ingester)
|
state_manager=blank_state, ingester=yaml_ingester)
|
||||||
orch_task = orchestrator.create_task(
|
orch_task = orchestrator.create_task(
|
||||||
action=hd_fields.OrchestratorAction.Noop)
|
action=hd_fields.OrchestratorAction.Noop,
|
||||||
|
design_ref=design_ref)
|
||||||
|
|
||||||
orch_task.set_status(hd_fields.TaskStatus.Queued)
|
orch_task.set_status(hd_fields.TaskStatus.Queued)
|
||||||
orch_task.save()
|
orch_task.save()
|
||||||
|
@ -38,6 +38,33 @@ class TestPostgres(object):
|
|||||||
|
|
||||||
assert result
|
assert result
|
||||||
|
|
||||||
|
def task_task_node_filter(self, blank_state):
|
||||||
|
"""Test that a persisted task persists node filter."""
|
||||||
|
ctx = DrydockRequestContext()
|
||||||
|
ctx.user = 'sh8121'
|
||||||
|
ctx.external_marker = str(uuid.uuid4())
|
||||||
|
|
||||||
|
node_filter = {
|
||||||
|
'filter_set_type': 'union',
|
||||||
|
'filter_set': [{
|
||||||
|
'node_names': ['foo'],
|
||||||
|
'filter_type': 'union'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
task = objects.Task(
|
||||||
|
action='deploy_node',
|
||||||
|
node_filter=node_filter,
|
||||||
|
design_ref='http://foo.bar/design',
|
||||||
|
context=ctx)
|
||||||
|
|
||||||
|
result = blank_state.post_task(task)
|
||||||
|
|
||||||
|
assert result
|
||||||
|
|
||||||
|
saved_task = blank_state.get_task(task.get_id())
|
||||||
|
|
||||||
|
assert saved_task.node_filter == node_filter
|
||||||
|
|
||||||
def test_subtask_append(self, blank_state):
|
def test_subtask_append(self, blank_state):
|
||||||
"""Test that the atomic subtask append method works."""
|
"""Test that the atomic subtask append method works."""
|
||||||
|
|
||||||
|
81
tests/unit/test_task_node_filter.py
Normal file
81
tests/unit/test_task_node_filter.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
"""Test the node filter logic in the orchestrator."""
|
||||||
|
|
||||||
|
import drydock_provisioner.objects as objects
|
||||||
|
import drydock_provisioner.objects.fields as hd_fields
|
||||||
|
|
||||||
|
|
||||||
|
class TestTaskFilterGeneration(object):
|
||||||
|
def test_task_success_focus(self, setup):
|
||||||
|
"""Test that marking a task successful works correctly."""
|
||||||
|
task = objects.Task(
|
||||||
|
action=hd_fields.OrchestratorAction.Noop,
|
||||||
|
design_ref="http://foo.com")
|
||||||
|
|
||||||
|
task.success(focus='foo')
|
||||||
|
|
||||||
|
assert task.result.status == hd_fields.ActionResult.Success
|
||||||
|
assert 'foo' in task.result.successes
|
||||||
|
|
||||||
|
def test_task_failure_focus(self, setup):
|
||||||
|
"""Test that marking a task failed works correctly."""
|
||||||
|
task = objects.Task(
|
||||||
|
action=hd_fields.OrchestratorAction.Noop,
|
||||||
|
design_ref="http://foo.com")
|
||||||
|
|
||||||
|
task.failure(focus='foo')
|
||||||
|
|
||||||
|
assert task.result.status == hd_fields.ActionResult.Failure
|
||||||
|
assert 'foo' in task.result.failures
|
||||||
|
|
||||||
|
def test_task_success_nf(self, setup):
|
||||||
|
"""Test that a task can generate a node filter based on its success."""
|
||||||
|
task = objects.Task(
|
||||||
|
action=hd_fields.OrchestratorAction.Noop,
|
||||||
|
design_ref="http://foo.com")
|
||||||
|
|
||||||
|
expected_nf = {
|
||||||
|
'filter_set_type': 'intersection',
|
||||||
|
'filter_set': [{
|
||||||
|
'node_names': ['foo'],
|
||||||
|
'filter_type': 'union',
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
task.success(focus='foo')
|
||||||
|
|
||||||
|
nf = task.node_filter_from_successes()
|
||||||
|
|
||||||
|
assert nf == expected_nf
|
||||||
|
|
||||||
|
def test_task_failure_nf(self, setup):
|
||||||
|
"""Test that a task can generate a node filter based on its failure."""
|
||||||
|
task = objects.Task(
|
||||||
|
action=hd_fields.OrchestratorAction.Noop,
|
||||||
|
design_ref="http://foo.com")
|
||||||
|
|
||||||
|
expected_nf = {
|
||||||
|
'filter_set_type': 'intersection',
|
||||||
|
'filter_set': [{
|
||||||
|
'node_names': ['foo'],
|
||||||
|
'filter_type': 'union',
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
task.failure(focus='foo')
|
||||||
|
|
||||||
|
nf = task.node_filter_from_failures()
|
||||||
|
|
||||||
|
assert nf == expected_nf
|
Loading…
Reference in New Issue
Block a user