Remove agent vendor passthru from OneView drivers
Change-Id: I811508fb1636f1c36a20a679e6555739c68ce7da Partial-Bug: #1640533
This commit is contained in:
parent
b7f001f441
commit
cf81c491f3
@ -19,12 +19,16 @@ import abc
|
||||
from futurist import periodics
|
||||
from ironic_lib import metrics_utils
|
||||
from oslo_log import log as logging
|
||||
import retrying
|
||||
import six
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common.i18n import _LE, _LI
|
||||
from ironic.common.i18n import _LE, _LI, _LW
|
||||
from ironic.common import states
|
||||
from ironic.conductor import utils as manager_utils
|
||||
from ironic.drivers.modules import agent
|
||||
from ironic.drivers.modules import agent_base_vendor
|
||||
from ironic.drivers.modules import deploy_utils as ironic_deploy_utils
|
||||
from ironic.drivers.modules import iscsi_deploy
|
||||
from ironic.drivers.modules.oneview import common
|
||||
from ironic.drivers.modules.oneview import deploy_utils
|
||||
@ -261,7 +265,94 @@ class OneViewIscsiDeploy(iscsi_deploy.ISCSIDeploy, OneViewPeriodicTasks):
|
||||
return super(OneViewIscsiDeploy, self).tear_down_cleaning(task)
|
||||
|
||||
|
||||
class OneViewAgentDeploy(agent.AgentDeploy, OneViewPeriodicTasks):
|
||||
# NOTE (thiagop): We overwrite this interface because we cannot change the boot
|
||||
# device of OneView managed blades while they are still powered on. We moved
|
||||
# the call of node_set_boot_device from reboot_to_instance to
|
||||
# reboot_and_finish_deploy and changed the behavior to shutdown the node before
|
||||
# doing it.
|
||||
# TODO(thiagop): remove this interface once bug/1503855 is fixed
|
||||
class OneViewAgentDeployMixin(object):
|
||||
|
||||
@METRICS.timer('OneViewAgentDeployMixin.reboot_to_instance')
|
||||
def reboot_to_instance(self, task, **kwargs):
|
||||
task.process_event('resume')
|
||||
node = task.node
|
||||
error = self.check_deploy_success(node)
|
||||
if error is not None:
|
||||
# TODO(jimrollenhagen) power off if using neutron dhcp to
|
||||
# align with pxe driver?
|
||||
msg = (_('node %(node)s command status errored: %(error)s') %
|
||||
{'node': node.uuid, 'error': error})
|
||||
LOG.error(msg)
|
||||
ironic_deploy_utils.set_failed_state(task, msg)
|
||||
return
|
||||
|
||||
LOG.info(_LI('Image successfully written to node %s'), node.uuid)
|
||||
LOG.debug('Rebooting node %s to instance', node.uuid)
|
||||
|
||||
self.reboot_and_finish_deploy(task)
|
||||
|
||||
# NOTE(TheJulia): If we deployed a whole disk image, we
|
||||
# should expect a whole disk image and clean-up the tftp files
|
||||
# on-disk incase the node is disregarding the boot preference.
|
||||
# TODO(rameshg87): Not all in-tree drivers using reboot_to_instance
|
||||
# have a boot interface. So include a check for now. Remove this
|
||||
# check once all in-tree drivers have a boot interface.
|
||||
if task.driver.boot:
|
||||
task.driver.boot.clean_up_ramdisk(task)
|
||||
|
||||
@METRICS.timer('OneViewAgentDeployMixin.reboot_and_finish_deploy')
|
||||
def reboot_and_finish_deploy(self, task):
|
||||
"""Helper method to trigger reboot on the node and finish deploy.
|
||||
|
||||
This method initiates a reboot on the node. On success, it
|
||||
marks the deploy as complete. On failure, it logs the error
|
||||
and marks deploy as failure.
|
||||
|
||||
:param task: a TaskManager object containing the node
|
||||
:raises: InstanceDeployFailure, if node reboot failed.
|
||||
"""
|
||||
wait = CONF.agent.post_deploy_get_power_state_retry_interval * 1000
|
||||
attempts = CONF.agent.post_deploy_get_power_state_retries + 1
|
||||
|
||||
@retrying.retry(
|
||||
stop_max_attempt_number=attempts,
|
||||
retry_on_result=lambda state: state != states.POWER_OFF,
|
||||
wait_fixed=wait
|
||||
)
|
||||
def _wait_until_powered_off(task):
|
||||
return task.driver.power.get_power_state(task)
|
||||
|
||||
node = task.node
|
||||
|
||||
try:
|
||||
try:
|
||||
self._client.power_off(node)
|
||||
_wait_until_powered_off(task)
|
||||
except Exception as e:
|
||||
LOG.warning(
|
||||
_LW('Failed to soft power off node %(node_uuid)s '
|
||||
'in at least %(timeout)d seconds. Error: %(error)s'),
|
||||
{'node_uuid': node.uuid,
|
||||
'timeout': (wait * (attempts - 1)) / 1000,
|
||||
'error': e})
|
||||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
|
||||
manager_utils.node_set_boot_device(task, 'disk',
|
||||
persistent=True)
|
||||
manager_utils.node_power_action(task, states.POWER_ON)
|
||||
except Exception as e:
|
||||
msg = (_('Error rebooting node %(node)s after deploy. '
|
||||
'Error: %(error)s') %
|
||||
{'node': node.uuid, 'error': e})
|
||||
agent_base_vendor.log_and_raise_deployment_error(task, msg)
|
||||
|
||||
task.process_event('done')
|
||||
LOG.info(_LI('Deployment to node %s done'), task.node.uuid)
|
||||
|
||||
|
||||
class OneViewAgentDeploy(OneViewAgentDeployMixin, agent.AgentDeploy,
|
||||
OneViewPeriodicTasks):
|
||||
"""Class for OneView Agent deployment driver."""
|
||||
|
||||
oneview_driver = common.AGENT_PXE_ONEVIEW
|
||||
|
@ -1,118 +0,0 @@
|
||||
#
|
||||
# Copyright 2015 Hewlett Packard Development Company, LP
|
||||
# Copyright 2015 Universidade Federal de Campina Grande
|
||||
#
|
||||
# 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.
|
||||
|
||||
from ironic_lib import metrics_utils
|
||||
from oslo_log import log
|
||||
import retrying
|
||||
|
||||
from ironic.common.i18n import _, _LI, _LW
|
||||
from ironic.common import states
|
||||
from ironic.conductor import utils as manager_utils
|
||||
from ironic.drivers.modules import agent
|
||||
from ironic.drivers.modules import agent_base_vendor
|
||||
from ironic.drivers.modules import deploy_utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
METRICS = metrics_utils.get_metrics_logger(__name__)
|
||||
|
||||
CONF = agent.CONF
|
||||
|
||||
|
||||
# NOTE (thiagop): We overwrite this interface because we cannot change the boot
|
||||
# device of OneView managed blades while they are still powered on. We moved
|
||||
# the call of node_set_boot_device from reboot_to_instance to
|
||||
# reboot_and_finish_deploy and changed the behavior to shutdown the node before
|
||||
# doing it.
|
||||
# TODO(thiagop): remove this interface once bug/1503855 is fixed
|
||||
class AgentVendorInterface(agent.AgentVendorInterface):
|
||||
|
||||
@METRICS.timer('AgentVendorInterface.reboot_to_instance')
|
||||
def reboot_to_instance(self, task, **kwargs):
|
||||
task.process_event('resume')
|
||||
node = task.node
|
||||
error = self.check_deploy_success(node)
|
||||
if error is not None:
|
||||
# TODO(jimrollenhagen) power off if using neutron dhcp to
|
||||
# align with pxe driver?
|
||||
msg = (_('node %(node)s command status errored: %(error)s') %
|
||||
{'node': node.uuid, 'error': error})
|
||||
LOG.error(msg)
|
||||
deploy_utils.set_failed_state(task, msg)
|
||||
return
|
||||
|
||||
LOG.info(_LI('Image successfully written to node %s'), node.uuid)
|
||||
LOG.debug('Rebooting node %s to instance', node.uuid)
|
||||
|
||||
self.reboot_and_finish_deploy(task)
|
||||
|
||||
# NOTE(TheJulia): If we deployed a whole disk image, we
|
||||
# should expect a whole disk image and clean-up the tftp files
|
||||
# on-disk incase the node is disregarding the boot preference.
|
||||
# TODO(rameshg87): Not all in-tree drivers using reboot_to_instance
|
||||
# have a boot interface. So include a check for now. Remove this
|
||||
# check once all in-tree drivers have a boot interface.
|
||||
if task.driver.boot:
|
||||
task.driver.boot.clean_up_ramdisk(task)
|
||||
|
||||
@METRICS.timer('AgentVendorInterface.reboot_and_finish_deploy')
|
||||
def reboot_and_finish_deploy(self, task):
|
||||
"""Helper method to trigger reboot on the node and finish deploy.
|
||||
|
||||
This method initiates a reboot on the node. On success, it
|
||||
marks the deploy as complete. On failure, it logs the error
|
||||
and marks deploy as failure.
|
||||
|
||||
:param task: a TaskManager object containing the node
|
||||
:raises: InstanceDeployFailure, if node reboot failed.
|
||||
"""
|
||||
wait = CONF.agent.post_deploy_get_power_state_retry_interval * 1000
|
||||
attempts = CONF.agent.post_deploy_get_power_state_retries + 1
|
||||
|
||||
@retrying.retry(
|
||||
stop_max_attempt_number=attempts,
|
||||
retry_on_result=lambda state: state != states.POWER_OFF,
|
||||
wait_fixed=wait
|
||||
)
|
||||
def _wait_until_powered_off(task):
|
||||
return task.driver.power.get_power_state(task)
|
||||
|
||||
node = task.node
|
||||
|
||||
try:
|
||||
try:
|
||||
self._client.power_off(node)
|
||||
_wait_until_powered_off(task)
|
||||
except Exception as e:
|
||||
LOG.warning(
|
||||
_LW('Failed to soft power off node %(node_uuid)s '
|
||||
'in at least %(timeout)d seconds. Error: %(error)s'),
|
||||
{'node_uuid': node.uuid,
|
||||
'timeout': (wait * (attempts - 1)) / 1000,
|
||||
'error': e})
|
||||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
|
||||
manager_utils.node_set_boot_device(task, 'disk',
|
||||
persistent=True)
|
||||
manager_utils.node_power_action(task, states.POWER_ON)
|
||||
except Exception as e:
|
||||
msg = (_('Error rebooting node %(node)s after deploy. '
|
||||
'Error: %(error)s') %
|
||||
{'node': node.uuid, 'error': e})
|
||||
agent_base_vendor.log_and_raise_deployment_error(task, msg)
|
||||
|
||||
task.process_event('done')
|
||||
LOG.info(_LI('Deployment to node %s done'), task.node.uuid)
|
@ -21,13 +21,11 @@ from oslo_utils import importutils
|
||||
from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules import iscsi_deploy
|
||||
from ironic.drivers.modules.oneview import common
|
||||
from ironic.drivers.modules.oneview import deploy
|
||||
from ironic.drivers.modules.oneview import inspect
|
||||
from ironic.drivers.modules.oneview import management
|
||||
from ironic.drivers.modules.oneview import power
|
||||
from ironic.drivers.modules.oneview import vendor
|
||||
from ironic.drivers.modules import pxe
|
||||
|
||||
|
||||
@ -55,7 +53,6 @@ class AgentPXEOneViewDriver(base.BaseDriver):
|
||||
self.management = management.OneViewManagement()
|
||||
self.boot = pxe.PXEBoot()
|
||||
self.deploy = deploy.OneViewAgentDeploy()
|
||||
self.vendor = vendor.AgentVendorInterface()
|
||||
self.inspect = inspect.OneViewInspect.create_if_enabled(
|
||||
'AgentPXEOneViewDriver')
|
||||
|
||||
@ -84,6 +81,5 @@ class ISCSIPXEOneViewDriver(base.BaseDriver):
|
||||
self.management = management.OneViewManagement()
|
||||
self.boot = pxe.PXEBoot()
|
||||
self.deploy = deploy.OneViewIscsiDeploy()
|
||||
self.vendor = iscsi_deploy.VendorPassthru()
|
||||
self.inspect = inspect.OneViewInspect.create_if_enabled(
|
||||
'ISCSIPXEOneViewDriver')
|
||||
|
@ -15,15 +15,23 @@
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import time
|
||||
import types
|
||||
|
||||
from oslo_utils import importutils
|
||||
|
||||
from ironic.common import driver_factory
|
||||
from ironic.common import exception
|
||||
from ironic.common import states
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.conductor import utils as manager_utils
|
||||
from ironic.drivers.modules import agent_client
|
||||
from ironic.drivers.modules.oneview import common
|
||||
from ironic.drivers.modules.oneview import deploy
|
||||
from ironic.drivers.modules.oneview import deploy_utils
|
||||
from ironic.drivers.modules.oneview import power
|
||||
from ironic.drivers.modules import pxe
|
||||
from ironic.drivers import utils as driver_utils
|
||||
from ironic.tests.unit.conductor import mgr_utils
|
||||
from ironic.tests.unit.db import base as db_base
|
||||
from ironic.tests.unit.db import utils as db_utils
|
||||
@ -42,6 +50,8 @@ nodes_freed_by_oneview = [(1, 'fake_oneview', maintenance_reason)]
|
||||
nodes_taken_on_cleanfail = [(1, 'fake_oneview', driver_internal_info)]
|
||||
nodes_taken_on_cleanfail_no_info = [(1, 'fake_oneview', {})]
|
||||
|
||||
GET_POWER_STATE_RETRIES = 5
|
||||
|
||||
|
||||
def _setup_node_in_available_state(node):
|
||||
node.provision_state = states.AVAILABLE
|
||||
@ -235,3 +245,266 @@ class OneViewPeriodicTasks(db_base.DbTestCase):
|
||||
self.assertNotEqual(common.NODE_IN_USE_BY_ONEVIEW,
|
||||
self.node.maintenance_reason)
|
||||
self.assertFalse('oneview_error' in self.node.driver_internal_info)
|
||||
|
||||
|
||||
@mock.patch.object(common, 'get_oneview_client', spec_set=True, autospec=True)
|
||||
class TestOneViewAgentDeploy(db_base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestOneViewAgentDeploy, self).setUp()
|
||||
self.config(
|
||||
post_deploy_get_power_state_retries=GET_POWER_STATE_RETRIES,
|
||||
group='agent')
|
||||
mgr_utils.mock_the_extension_manager(driver="agent_pxe_oneview")
|
||||
|
||||
self.node = obj_utils.create_test_node(
|
||||
self.context, driver='agent_pxe_oneview',
|
||||
properties=db_utils.get_test_oneview_properties(),
|
||||
driver_info=db_utils.get_test_oneview_driver_info(),
|
||||
driver_internal_info={'agent_url': 'http://1.2.3.4:5678'},
|
||||
)
|
||||
|
||||
@mock.patch.object(time, 'sleep', lambda seconds: None)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(power.OneViewPower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch('ironic.conductor.utils.node_set_boot_device', autospec=True)
|
||||
def test_reboot_and_finish_deploy(self, set_bootdev_mock, power_off_mock,
|
||||
get_power_state_mock,
|
||||
node_power_action_mock,
|
||||
mock_get_ov_client):
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
get_power_state_mock.side_effect = [states.POWER_ON,
|
||||
states.POWER_OFF]
|
||||
task.driver.deploy.reboot_and_finish_deploy(task)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
self.assertEqual(2, get_power_state_mock.call_count)
|
||||
set_bootdev_mock.assert_called_once_with(task, 'disk',
|
||||
persistent=True)
|
||||
node_power_action_mock.assert_called_once_with(
|
||||
task, states.POWER_ON)
|
||||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(time, 'sleep', lambda seconds: None)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(power.OneViewPower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
def test_reboot_and_finish_deploy_soft_poweroff_doesnt_complete(
|
||||
self, power_off_mock, get_power_state_mock,
|
||||
node_power_action_mock, mock_get_ov_client):
|
||||
client = mock_get_ov_client.return_value
|
||||
fake_server_hardware = oneview_models.ServerHardware()
|
||||
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
|
||||
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
|
||||
mock_get_ov_client.return_value = client
|
||||
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
driver_info = self.node.driver_info
|
||||
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
|
||||
self.node.driver_info = driver_info
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
get_power_state_mock.return_value = states.POWER_ON
|
||||
task.driver.deploy.reboot_and_finish_deploy(task)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
self.assertEqual(GET_POWER_STATE_RETRIES + 1,
|
||||
get_power_state_mock.call_count)
|
||||
node_power_action_mock.assert_has_calls([
|
||||
mock.call(task, states.POWER_OFF),
|
||||
mock.call(task, states.POWER_ON)
|
||||
])
|
||||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
def test_reboot_and_finish_deploy_soft_poweroff_fails(
|
||||
self, power_off_mock, node_power_action_mock,
|
||||
mock_get_ov_client):
|
||||
client = mock_get_ov_client.return_value
|
||||
fake_server_hardware = oneview_models.ServerHardware()
|
||||
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
|
||||
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
|
||||
mock_get_ov_client.return_value = client
|
||||
|
||||
power_off_mock.side_effect = RuntimeError("boom")
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
driver_info = self.node.driver_info
|
||||
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
|
||||
self.node.driver_info = driver_info
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.deploy.reboot_and_finish_deploy(task)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
node_power_action_mock.assert_has_calls([
|
||||
mock.call(task, states.POWER_OFF),
|
||||
mock.call(task, states.POWER_ON)
|
||||
])
|
||||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(time, 'sleep', lambda seconds: None)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(power.OneViewPower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
def test_reboot_and_finish_deploy_get_power_state_fails(
|
||||
self, power_off_mock, get_power_state_mock,
|
||||
node_power_action_mock, mock_get_ov_client):
|
||||
client = mock_get_ov_client.return_value
|
||||
fake_server_hardware = oneview_models.ServerHardware()
|
||||
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
|
||||
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
|
||||
mock_get_ov_client.return_value = client
|
||||
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
driver_info = self.node.driver_info
|
||||
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
|
||||
self.node.driver_info = driver_info
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
get_power_state_mock.side_effect = RuntimeError("boom")
|
||||
task.driver.deploy.reboot_and_finish_deploy(task)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
self.assertEqual(GET_POWER_STATE_RETRIES + 1,
|
||||
get_power_state_mock.call_count)
|
||||
node_power_action_mock.assert_has_calls([
|
||||
mock.call(task, states.POWER_OFF),
|
||||
mock.call(task, states.POWER_ON)
|
||||
])
|
||||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(driver_utils, 'collect_ramdisk_logs', autospec=True)
|
||||
@mock.patch.object(time, 'sleep', lambda seconds: None)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(power.OneViewPower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
def test_reboot_and_finish_deploy_power_action_fails(
|
||||
self, power_off_mock, get_power_state_mock,
|
||||
node_power_action_mock, collect_ramdisk_logs_mock,
|
||||
mock_get_ov_client):
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
get_power_state_mock.return_value = states.POWER_ON
|
||||
node_power_action_mock.side_effect = RuntimeError("boom")
|
||||
self.assertRaises(exception.InstanceDeployFailure,
|
||||
task.driver.deploy.reboot_and_finish_deploy,
|
||||
task)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
self.assertEqual(GET_POWER_STATE_RETRIES + 1,
|
||||
get_power_state_mock.call_count)
|
||||
node_power_action_mock.assert_has_calls([
|
||||
mock.call(task, states.POWER_OFF),
|
||||
mock.call(task, states.POWER_OFF)])
|
||||
self.assertEqual(states.DEPLOYFAIL, task.node.provision_state)
|
||||
self.assertEqual(states.ACTIVE, task.node.target_provision_state)
|
||||
collect_ramdisk_logs_mock.assert_called_once_with(task.node)
|
||||
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(power.OneViewPower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch('ironic.drivers.modules.agent.AgentDeploy'
|
||||
'.check_deploy_success', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'clean_up_ramdisk', autospec=True)
|
||||
def test_reboot_to_instance(self, clean_pxe_mock, check_deploy_mock,
|
||||
power_off_mock, get_power_state_mock,
|
||||
node_power_action_mock, mock_get_ov_client):
|
||||
check_deploy_mock.return_value = None
|
||||
|
||||
client = mock_get_ov_client.return_value
|
||||
fake_server_hardware = oneview_models.ServerHardware()
|
||||
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
|
||||
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
|
||||
mock_get_ov_client.return_value = client
|
||||
|
||||
self.node.provision_state = states.DEPLOYWAIT
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
driver_info = self.node.driver_info
|
||||
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
|
||||
self.node.driver_info = driver_info
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
get_power_state_mock.return_value = states.POWER_OFF
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = True
|
||||
|
||||
task.driver.deploy.reboot_to_instance(task)
|
||||
|
||||
clean_pxe_mock.assert_called_once_with(task.driver.boot, task)
|
||||
check_deploy_mock.assert_called_once_with(mock.ANY, task.node)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
get_power_state_mock.assert_called_once_with(task)
|
||||
node_power_action_mock.assert_called_once_with(
|
||||
task, states.POWER_ON)
|
||||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(power.OneViewPower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch('ironic.drivers.modules.agent.AgentDeploy'
|
||||
'.check_deploy_success', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'clean_up_ramdisk', autospec=True)
|
||||
def test_reboot_to_instance_boot_none(self, clean_pxe_mock,
|
||||
check_deploy_mock,
|
||||
power_off_mock,
|
||||
get_power_state_mock,
|
||||
node_power_action_mock,
|
||||
mock_get_ov_client):
|
||||
client = mock_get_ov_client.return_value
|
||||
fake_server_hardware = oneview_models.ServerHardware()
|
||||
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
|
||||
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
|
||||
mock_get_ov_client.return_value = client
|
||||
|
||||
check_deploy_mock.return_value = None
|
||||
|
||||
self.node.provision_state = states.DEPLOYWAIT
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
driver_info = self.node.driver_info
|
||||
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
|
||||
self.node.driver_info = driver_info
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
get_power_state_mock.return_value = states.POWER_OFF
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = True
|
||||
task.driver.boot = None
|
||||
|
||||
task.driver.deploy.reboot_to_instance(task)
|
||||
|
||||
self.assertFalse(clean_pxe_mock.called)
|
||||
check_deploy_mock.assert_called_once_with(mock.ANY, task.node)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
get_power_state_mock.assert_called_once_with(task)
|
||||
node_power_action_mock.assert_called_once_with(
|
||||
task, states.POWER_ON)
|
||||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
|
@ -1,305 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2015 Red Hat, Inc.
|
||||
# All 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.
|
||||
|
||||
import mock
|
||||
import time
|
||||
import types
|
||||
|
||||
from oslo_utils import importutils
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common import states
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.conductor import utils as manager_utils
|
||||
from ironic.drivers.modules import agent_client
|
||||
from ironic.drivers.modules.oneview import common
|
||||
from ironic.drivers.modules.oneview import power
|
||||
from ironic.drivers.modules.oneview import vendor
|
||||
from ironic.drivers.modules import pxe
|
||||
from ironic.drivers import utils as driver_utils
|
||||
from ironic.tests.unit.conductor import mgr_utils
|
||||
from ironic.tests.unit.db import base as db_base
|
||||
from ironic.tests.unit.db import utils as db_utils
|
||||
from ironic.tests.unit.objects import utils as obj_utils
|
||||
|
||||
oneview_models = importutils.try_import('oneview_client.models')
|
||||
|
||||
|
||||
GET_POWER_STATE_RETRIES = 5
|
||||
|
||||
|
||||
@mock.patch.object(common, 'get_oneview_client', spec_set=True, autospec=True)
|
||||
class TestBaseAgentVendor(db_base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestBaseAgentVendor, self).setUp()
|
||||
self.config(
|
||||
post_deploy_get_power_state_retries=GET_POWER_STATE_RETRIES,
|
||||
group='agent')
|
||||
mgr_utils.mock_the_extension_manager(driver="agent_pxe_oneview")
|
||||
self.passthru = vendor.AgentVendorInterface()
|
||||
|
||||
self.node = obj_utils.create_test_node(
|
||||
self.context, driver='agent_pxe_oneview',
|
||||
properties=db_utils.get_test_oneview_properties(),
|
||||
driver_info=db_utils.get_test_oneview_driver_info(),
|
||||
)
|
||||
|
||||
@mock.patch.object(time, 'sleep', lambda seconds: None)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(power.OneViewPower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch('ironic.conductor.utils.node_set_boot_device', autospec=True)
|
||||
def test_reboot_and_finish_deploy(self, set_bootdev_mock, power_off_mock,
|
||||
get_power_state_mock,
|
||||
node_power_action_mock,
|
||||
mock_get_ov_client):
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
get_power_state_mock.side_effect = [states.POWER_ON,
|
||||
states.POWER_OFF]
|
||||
self.passthru.reboot_and_finish_deploy(task)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
self.assertEqual(2, get_power_state_mock.call_count)
|
||||
set_bootdev_mock.assert_called_once_with(task, 'disk',
|
||||
persistent=True)
|
||||
node_power_action_mock.assert_called_once_with(
|
||||
task, states.POWER_ON)
|
||||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(time, 'sleep', lambda seconds: None)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(power.OneViewPower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
def test_reboot_and_finish_deploy_soft_poweroff_doesnt_complete(
|
||||
self, power_off_mock, get_power_state_mock,
|
||||
node_power_action_mock, mock_get_ov_client):
|
||||
client = mock_get_ov_client.return_value
|
||||
fake_server_hardware = oneview_models.ServerHardware()
|
||||
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
|
||||
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
|
||||
mock_get_ov_client.return_value = client
|
||||
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
driver_info = self.node.driver_info
|
||||
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
|
||||
self.node.driver_info = driver_info
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
get_power_state_mock.return_value = states.POWER_ON
|
||||
self.passthru.reboot_and_finish_deploy(task)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
self.assertEqual(GET_POWER_STATE_RETRIES + 1,
|
||||
get_power_state_mock.call_count)
|
||||
node_power_action_mock.assert_has_calls([
|
||||
mock.call(task, states.POWER_OFF),
|
||||
mock.call(task, states.POWER_ON)
|
||||
])
|
||||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
def test_reboot_and_finish_deploy_soft_poweroff_fails(
|
||||
self, power_off_mock, node_power_action_mock,
|
||||
mock_get_ov_client):
|
||||
client = mock_get_ov_client.return_value
|
||||
fake_server_hardware = oneview_models.ServerHardware()
|
||||
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
|
||||
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
|
||||
mock_get_ov_client.return_value = client
|
||||
|
||||
power_off_mock.side_effect = RuntimeError("boom")
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
driver_info = self.node.driver_info
|
||||
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
|
||||
self.node.driver_info = driver_info
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
self.passthru.reboot_and_finish_deploy(task)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
node_power_action_mock.assert_has_calls([
|
||||
mock.call(task, states.POWER_OFF),
|
||||
mock.call(task, states.POWER_ON)
|
||||
])
|
||||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(time, 'sleep', lambda seconds: None)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(power.OneViewPower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
def test_reboot_and_finish_deploy_get_power_state_fails(
|
||||
self, power_off_mock, get_power_state_mock,
|
||||
node_power_action_mock, mock_get_ov_client):
|
||||
client = mock_get_ov_client.return_value
|
||||
fake_server_hardware = oneview_models.ServerHardware()
|
||||
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
|
||||
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
|
||||
mock_get_ov_client.return_value = client
|
||||
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
driver_info = self.node.driver_info
|
||||
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
|
||||
self.node.driver_info = driver_info
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
get_power_state_mock.side_effect = RuntimeError("boom")
|
||||
self.passthru.reboot_and_finish_deploy(task)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
self.assertEqual(GET_POWER_STATE_RETRIES + 1,
|
||||
get_power_state_mock.call_count)
|
||||
node_power_action_mock.assert_has_calls([
|
||||
mock.call(task, states.POWER_OFF),
|
||||
mock.call(task, states.POWER_ON)
|
||||
])
|
||||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(driver_utils, 'collect_ramdisk_logs', autospec=True)
|
||||
@mock.patch.object(time, 'sleep', lambda seconds: None)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(power.OneViewPower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
def test_reboot_and_finish_deploy_power_action_fails(
|
||||
self, power_off_mock, get_power_state_mock,
|
||||
node_power_action_mock, collect_ramdisk_logs_mock,
|
||||
mock_get_ov_client):
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
get_power_state_mock.return_value = states.POWER_ON
|
||||
node_power_action_mock.side_effect = RuntimeError("boom")
|
||||
self.assertRaises(exception.InstanceDeployFailure,
|
||||
self.passthru.reboot_and_finish_deploy,
|
||||
task)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
self.assertEqual(GET_POWER_STATE_RETRIES + 1,
|
||||
get_power_state_mock.call_count)
|
||||
node_power_action_mock.assert_has_calls([
|
||||
mock.call(task, states.POWER_OFF),
|
||||
mock.call(task, states.POWER_OFF)])
|
||||
self.assertEqual(states.DEPLOYFAIL, task.node.provision_state)
|
||||
self.assertEqual(states.ACTIVE, task.node.target_provision_state)
|
||||
collect_ramdisk_logs_mock.assert_called_once_with(task.node)
|
||||
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(power.OneViewPower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch('ironic.drivers.modules.agent.AgentVendorInterface'
|
||||
'.check_deploy_success', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'clean_up_ramdisk', autospec=True)
|
||||
def test_reboot_to_instance(self, clean_pxe_mock, check_deploy_mock,
|
||||
power_off_mock, get_power_state_mock,
|
||||
node_power_action_mock, mock_get_ov_client):
|
||||
check_deploy_mock.return_value = None
|
||||
|
||||
client = mock_get_ov_client.return_value
|
||||
fake_server_hardware = oneview_models.ServerHardware()
|
||||
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
|
||||
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
|
||||
mock_get_ov_client.return_value = client
|
||||
|
||||
self.node.provision_state = states.DEPLOYWAIT
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
driver_info = self.node.driver_info
|
||||
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
|
||||
self.node.driver_info = driver_info
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
get_power_state_mock.return_value = states.POWER_OFF
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = True
|
||||
|
||||
self.passthru.reboot_to_instance(task)
|
||||
|
||||
clean_pxe_mock.assert_called_once_with(task.driver.boot, task)
|
||||
check_deploy_mock.assert_called_once_with(mock.ANY, task.node)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
get_power_state_mock.assert_called_once_with(task)
|
||||
node_power_action_mock.assert_called_once_with(
|
||||
task, states.POWER_ON)
|
||||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(power.OneViewPower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch('ironic.drivers.modules.agent.AgentVendorInterface'
|
||||
'.check_deploy_success', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'clean_up_ramdisk', autospec=True)
|
||||
def test_reboot_to_instance_boot_none(self, clean_pxe_mock,
|
||||
check_deploy_mock,
|
||||
power_off_mock,
|
||||
get_power_state_mock,
|
||||
node_power_action_mock,
|
||||
mock_get_ov_client):
|
||||
client = mock_get_ov_client.return_value
|
||||
fake_server_hardware = oneview_models.ServerHardware()
|
||||
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
|
||||
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
|
||||
mock_get_ov_client.return_value = client
|
||||
|
||||
check_deploy_mock.return_value = None
|
||||
|
||||
self.node.provision_state = states.DEPLOYWAIT
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
driver_info = self.node.driver_info
|
||||
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
|
||||
self.node.driver_info = driver_info
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
get_power_state_mock.return_value = states.POWER_OFF
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = True
|
||||
task.driver.boot = None
|
||||
|
||||
self.passthru.reboot_to_instance(task)
|
||||
|
||||
self.assertFalse(clean_pxe_mock.called)
|
||||
check_deploy_mock.assert_called_once_with(mock.ANY, task.node)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
get_power_state_mock.assert_called_once_with(task)
|
||||
node_power_action_mock.assert_called_once_with(
|
||||
task, states.POWER_ON)
|
||||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
@ -0,0 +1,16 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Agent lookup/heartbeat as vendor passthru is removed from OneView drivers.
|
||||
Affected drivers are
|
||||
|
||||
* agent_pxe_oneview
|
||||
* iscsi_pxe_oneview
|
||||
|
||||
These drivers no longer have any vendor passthru methods.
|
||||
|
||||
upgrade:
|
||||
- Agent lookup/heartbeat as vendor passthru is removed from OneView drivers.
|
||||
That means that OneView drivers become incompatible with IPA < 1.5.0.
|
||||
Operators are required to update their IPA-based deploy ramdisks to
|
||||
contain IPA >= 1.5.0.
|
Loading…
x
Reference in New Issue
Block a user