Merge "Add support for Smart NICs"
This commit is contained in:
commit
b3c00fb118
|
@ -14,6 +14,7 @@ from neutronclient.common import exceptions as neutron_exceptions
|
|||
from neutronclient.v2_0 import client as clientv20
|
||||
from oslo_log import log
|
||||
from oslo_utils import uuidutils
|
||||
import retrying
|
||||
|
||||
from ironic.common import context as ironic_context
|
||||
from ironic.common import exception
|
||||
|
@ -21,6 +22,7 @@ from ironic.common.i18n import _
|
|||
from ironic.common import keystone
|
||||
from ironic.common.pxe_utils import DHCP_CLIENT_ID
|
||||
from ironic.conf import CONF
|
||||
from ironic import objects
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
@ -31,6 +33,7 @@ DEFAULT_NEUTRON_URL = 'http://%s:9696' % CONF.my_ip
|
|||
_NEUTRON_SESSION = None
|
||||
|
||||
VNIC_BAREMETAL = 'baremetal'
|
||||
VNIC_SMARTNIC = 'smart-nic'
|
||||
|
||||
PHYSNET_PARAM_NAME = 'provider:physical_network'
|
||||
"""Name of the neutron network API physical network parameter."""
|
||||
|
@ -256,6 +259,17 @@ def add_ports_to_network(task, network_uuid, security_groups=None):
|
|||
binding_profile = {'local_link_information':
|
||||
[portmap[ironic_port.uuid]]}
|
||||
body['port']['binding:profile'] = binding_profile
|
||||
link_info = binding_profile['local_link_information'][0]
|
||||
is_smart_nic = is_smartnic_port(ironic_port)
|
||||
if is_smart_nic:
|
||||
LOG.debug('Setting hostname as host_id in case of Smart NIC, '
|
||||
'port %(port_id)s, hostname %(hostname)s',
|
||||
{'port_id': ironic_port.uuid,
|
||||
'hostname': link_info['hostname']})
|
||||
body['port']['binding:host_id'] = link_info['hostname']
|
||||
|
||||
# TODO(hamdyk): use portbindings.VNIC_SMARTNIC from neutron-lib
|
||||
body['port']['binding:vnic_type'] = VNIC_SMARTNIC
|
||||
client_id = ironic_port.extra.get('client-id')
|
||||
if client_id:
|
||||
client_id_opt = {'opt_name': DHCP_CLIENT_ID,
|
||||
|
@ -264,7 +278,11 @@ def add_ports_to_network(task, network_uuid, security_groups=None):
|
|||
extra_dhcp_opts.append(client_id_opt)
|
||||
body['port']['extra_dhcp_opts'] = extra_dhcp_opts
|
||||
try:
|
||||
if is_smart_nic:
|
||||
wait_for_host_agent(client, body['port']['binding:host_id'])
|
||||
port = client.create_port(body)
|
||||
if is_smart_nic:
|
||||
wait_for_port_status(client, port['port']['id'], 'ACTIVE')
|
||||
except neutron_exceptions.NeutronClientException as e:
|
||||
failures.append(ironic_port.uuid)
|
||||
LOG.warning("Could not create neutron port for node's "
|
||||
|
@ -342,6 +360,8 @@ def remove_neutron_ports(task, params):
|
|||
'%(node_id)s.',
|
||||
{'vif_port_id': port['id'], 'node_id': node_uuid})
|
||||
|
||||
if is_smartnic_port(port):
|
||||
wait_for_host_agent(client, port['binding:host_id'])
|
||||
try:
|
||||
client.delete_port(port['id'])
|
||||
# NOTE(mgoddard): Ignore if the port was deleted by nova.
|
||||
|
@ -488,6 +508,44 @@ def validate_port_info(node, port):
|
|||
return True
|
||||
|
||||
|
||||
def validate_agent(client, **kwargs):
|
||||
"""Check that the given neutron agent is alive
|
||||
|
||||
:param client: Neutron client
|
||||
:param kwargs: Additional parameters to pass to the neutron client
|
||||
list_agents method.
|
||||
:returns: A boolean to describe the agent status, if more than one agent
|
||||
returns by the client then return True if at least one of them is
|
||||
alive.
|
||||
:raises: NetworkError in case of failure contacting Neutron.
|
||||
"""
|
||||
try:
|
||||
agents = client.list_agents(**kwargs)['agents']
|
||||
for agent in agents:
|
||||
if agent['alive']:
|
||||
return True
|
||||
return False
|
||||
except neutron_exceptions.NeutronClientException:
|
||||
raise exception.NetworkError('Failed to contact Neutron server')
|
||||
|
||||
|
||||
def is_smartnic_port(port_data):
|
||||
"""Check that the port is Smart NIC port
|
||||
|
||||
:param port_data: an instance of ironic.objects.port.Port
|
||||
or port data as dict.
|
||||
:returns: A boolean to indicate port as Smart NIC port.
|
||||
"""
|
||||
if isinstance(port_data, objects.Port):
|
||||
return port_data.supports_is_smartnic() and port_data.is_smartnic
|
||||
|
||||
if isinstance(port_data, dict):
|
||||
return port_data.get('is_smartnic', False)
|
||||
|
||||
LOG.warning('Unknown port data type: %(type)s', {'type': type(port_data)})
|
||||
return False
|
||||
|
||||
|
||||
def _get_network_by_uuid_or_name(client, uuid_or_name, net_type=_('network'),
|
||||
**params):
|
||||
"""Return a neutron network by UUID or name.
|
||||
|
@ -586,6 +644,72 @@ def get_physnets_by_port_uuid(client, port_uuid):
|
|||
if segment[PHYSNET_PARAM_NAME])
|
||||
|
||||
|
||||
@retrying.retry(
|
||||
stop_max_attempt_number=CONF.agent.neutron_agent_max_attempts,
|
||||
retry_on_exception=lambda e: isinstance(e, exception.NetworkError),
|
||||
wait_fixed=CONF.agent.neutron_agent_wait_time_seconds * 1000
|
||||
)
|
||||
def wait_for_host_agent(client, host_id, target_state='up'):
|
||||
"""Wait for neutron agent to become target state
|
||||
|
||||
:param client: A Neutron client object.
|
||||
:param host_id: Agent host_id
|
||||
:param target_state: up: wait for up status,
|
||||
down: wait for down status
|
||||
:returns: boolean indicates the agent state matches
|
||||
param value target_state_up.
|
||||
:raises: exception.NetworkError if host status didn't match the required
|
||||
status after max retry attempts.
|
||||
"""
|
||||
if target_state not in ['up', 'down']:
|
||||
raise exception.Invalid(
|
||||
'Invalid requested agent state to validate, accepted values: '
|
||||
'up, down. Requested state: %(target_state)s' % {
|
||||
'target_state': target_state})
|
||||
|
||||
LOG.debug('Validating host %(host_id)s agent is %(status)s',
|
||||
{'host_id': host_id,
|
||||
'status': target_state})
|
||||
is_alive = validate_agent(client, host=host_id)
|
||||
LOG.debug('Agent on host %(host_id)s is %(status)s',
|
||||
{'host_id': host_id,
|
||||
'status': 'up' if is_alive else 'down'})
|
||||
if ((target_state == 'up' and is_alive) or
|
||||
(target_state == 'down' and not is_alive)):
|
||||
return True
|
||||
raise exception.NetworkError(
|
||||
'Agent on host %(host)s failed to reach state %(state)s' % {
|
||||
'host': host_id, 'state': target_state})
|
||||
|
||||
|
||||
@retrying.retry(
|
||||
stop_max_attempt_number=CONF.agent.neutron_agent_max_attempts,
|
||||
retry_on_exception=lambda e: isinstance(e, exception.NetworkError),
|
||||
wait_fixed=CONF.agent.neutron_agent_wait_time_seconds * 1000
|
||||
)
|
||||
def wait_for_port_status(client, port_id, status):
|
||||
"""Wait for port status to be the desired status
|
||||
|
||||
:param client: A Neutron client object.
|
||||
:param port_id: Neutron port_id
|
||||
:param status: Port's target status, can be ACTIVE, DOWN ... etc.
|
||||
:returns: boolean indicates that the port status matches the
|
||||
required value passed by param status.
|
||||
:raises: exception.NetworkError if port status didn't match
|
||||
the required status after max retry attempts.
|
||||
"""
|
||||
LOG.debug('Validating Port %(port_id)s status is %(status)s',
|
||||
{'port_id': port_id, 'status': status})
|
||||
port_info = _get_port_by_uuid(client, port_id)
|
||||
LOG.debug('Port %(port_id)s status is: %(status)s',
|
||||
{'port_id': port_id, 'status': port_info['status']})
|
||||
if port_info['status'] == status:
|
||||
return True
|
||||
raise exception.NetworkError(
|
||||
'Port %(port_id)s failed to reach status %(status)s' % {
|
||||
'port_id': port_id, 'status': status})
|
||||
|
||||
|
||||
class NeutronNetworkInterfaceMixin(object):
|
||||
|
||||
def get_cleaning_network_uuid(self, task):
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
# 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 time
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
|
@ -18,6 +19,7 @@ from oslo_service import loopingcall
|
|||
from oslo_utils import excutils
|
||||
import six
|
||||
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import exception
|
||||
from ironic.common import faults
|
||||
from ironic.common.i18n import _
|
||||
|
@ -1000,3 +1002,55 @@ def skip_automated_cleaning(node):
|
|||
:param node: the node to consider
|
||||
"""
|
||||
return not CONF.conductor.automated_clean and not node.automated_clean
|
||||
|
||||
|
||||
def power_on_node_if_needed(task):
|
||||
"""Powers on node if it is powered off and has a Smart NIC port
|
||||
|
||||
:param task: A TaskManager object
|
||||
:returns: the previous power state or None if no changes were made
|
||||
"""
|
||||
if not task.driver.network.need_power_on(task):
|
||||
return
|
||||
|
||||
previous_power_state = task.driver.power.get_power_state(task)
|
||||
if previous_power_state == states.POWER_OFF:
|
||||
node_set_boot_device(
|
||||
task, boot_devices.BIOS, persistent=False)
|
||||
node_power_action(task, states.POWER_ON)
|
||||
|
||||
# local import is necessary to avoid circular import
|
||||
from ironic.common import neutron
|
||||
|
||||
host_id = None
|
||||
for port in task.ports:
|
||||
if neutron.is_smartnic_port(port):
|
||||
link_info = port.local_link_connection
|
||||
host_id = link_info['hostname']
|
||||
break
|
||||
|
||||
if host_id:
|
||||
LOG.debug('Waiting for host %(host)s agent to be down',
|
||||
{'host': host_id})
|
||||
|
||||
client = neutron.get_client(context=task.context)
|
||||
neutron.wait_for_host_agent(
|
||||
client, host_id, target_state='down')
|
||||
return previous_power_state
|
||||
|
||||
|
||||
def restore_power_state_if_needed(task, power_state_to_restore):
|
||||
"""Change the node's power state if power_state_to_restore is not empty
|
||||
|
||||
:param task: A TaskManager object
|
||||
:param power_state_to_restore: power state
|
||||
"""
|
||||
if power_state_to_restore:
|
||||
|
||||
# Sleep is required here in order to give neutron agent
|
||||
# a chance to apply the changes before powering off.
|
||||
# Using twice the polling interval of the agent
|
||||
# "CONF.AGENT.polling_interval" would give the agent
|
||||
# enough time to apply network changes.
|
||||
time.sleep(CONF.agent.neutron_agent_poll_interval * 2)
|
||||
node_power_action(task, power_state_to_restore)
|
||||
|
|
|
@ -110,6 +110,21 @@ opts = [
|
|||
help=_('This is the maximum number of attempts that will be '
|
||||
'done for IPA commands that fails due to network '
|
||||
'problems.')),
|
||||
cfg.IntOpt('neutron_agent_poll_interval',
|
||||
default=2,
|
||||
help=_('The number of seconds Neutron agent will wait between '
|
||||
'polling for device changes. This value should be '
|
||||
'the same as CONF.AGENT.polling_interval in Neutron '
|
||||
'configuration.')),
|
||||
cfg.IntOpt('neutron_agent_max_attempts',
|
||||
default=100,
|
||||
help=_('Max number of attempts to validate a Neutron agent '
|
||||
'status alive before raising network error for a '
|
||||
'dead agent.')),
|
||||
cfg.IntOpt('neutron_agent_wait_time_seconds',
|
||||
default=10,
|
||||
help=_('Wait time in seconds between attempts for validating '
|
||||
'Neutron agent status.')),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -1334,6 +1334,14 @@ class NetworkInterface(BaseInterface):
|
|||
"""
|
||||
pass
|
||||
|
||||
def need_power_on(self, task):
|
||||
"""Check if ironic node must be powered on before applying network changes
|
||||
|
||||
:param task: A TaskManager instance.
|
||||
:returns: Boolean.
|
||||
"""
|
||||
return False
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class StorageInterface(BaseInterface):
|
||||
|
|
|
@ -464,8 +464,12 @@ class AgentDeploy(AgentDeployMixin, base.DeployInterface):
|
|||
# This is not being done now as it is expected to be
|
||||
# refactored in the near future.
|
||||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
power_state_to_restore = (
|
||||
manager_utils.power_on_node_if_needed(task))
|
||||
task.driver.network.remove_provisioning_network(task)
|
||||
task.driver.network.configure_tenant_networks(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
task.driver.boot.prepare_instance(task)
|
||||
manager_utils.node_power_action(task, states.POWER_ON)
|
||||
LOG.info('Deployment to node %s done', task.node.uuid)
|
||||
|
@ -489,11 +493,13 @@ class AgentDeploy(AgentDeployMixin, base.DeployInterface):
|
|||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
task.driver.storage.detach_volumes(task)
|
||||
deploy_utils.tear_down_storage_configuration(task)
|
||||
power_state_to_restore = manager_utils.power_on_node_if_needed(task)
|
||||
task.driver.network.unconfigure_tenant_networks(task)
|
||||
# NOTE(mgoddard): If the deployment was unsuccessful the node may have
|
||||
# ports on the provisioning network which were not deleted.
|
||||
task.driver.network.remove_provisioning_network(task)
|
||||
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
return states.DELETED
|
||||
|
||||
@METRICS.timer('AgentDeploy.prepare')
|
||||
|
@ -548,8 +554,12 @@ class AgentDeploy(AgentDeployMixin, base.DeployInterface):
|
|||
if task.driver.storage.should_write_image(task):
|
||||
# NOTE(vdrok): in case of rebuild, we have tenant network
|
||||
# already configured, unbind tenant ports if present
|
||||
power_state_to_restore = (
|
||||
manager_utils.power_on_node_if_needed(task))
|
||||
task.driver.network.unconfigure_tenant_networks(task)
|
||||
task.driver.network.add_provisioning_network(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
# Signal to storage driver to attach volumes
|
||||
task.driver.storage.attach_volumes(task)
|
||||
if not task.driver.storage.should_write_image(task):
|
||||
|
@ -806,8 +816,11 @@ class AgentRescue(base.RescueInterface):
|
|||
task.node.save()
|
||||
|
||||
task.driver.boot.clean_up_instance(task)
|
||||
power_state_to_restore = manager_utils.power_on_node_if_needed(task)
|
||||
task.driver.network.unconfigure_tenant_networks(task)
|
||||
task.driver.network.add_rescuing_network(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
if CONF.agent.manage_agent_boot:
|
||||
ramdisk_opts = deploy_utils.build_agent_options(task.node)
|
||||
# prepare_ramdisk will set the boot device
|
||||
|
@ -842,7 +855,10 @@ class AgentRescue(base.RescueInterface):
|
|||
task.node.save()
|
||||
|
||||
self.clean_up(task)
|
||||
power_state_to_restore = manager_utils.power_on_node_if_needed(task)
|
||||
task.driver.network.configure_tenant_networks(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
task.driver.boot.prepare_instance(task)
|
||||
manager_utils.node_power_action(task, states.POWER_ON)
|
||||
|
||||
|
@ -894,4 +910,7 @@ class AgentRescue(base.RescueInterface):
|
|||
manager_utils.remove_node_rescue_password(task.node, save=True)
|
||||
if CONF.agent.manage_agent_boot:
|
||||
task.driver.boot.clean_up_ramdisk(task)
|
||||
power_state_to_restore = manager_utils.power_on_node_if_needed(task)
|
||||
task.driver.network.remove_rescuing_network(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
|
|
|
@ -373,7 +373,10 @@ class HeartbeatMixin(object):
|
|||
reason=fail_reason)
|
||||
task.process_event('resume')
|
||||
task.driver.rescue.clean_up(task)
|
||||
power_state_to_restore = manager_utils.power_on_node_if_needed(task)
|
||||
task.driver.network.configure_tenant_networks(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
task.process_event('done')
|
||||
|
||||
|
||||
|
@ -642,9 +645,12 @@ class AgentDeployMixin(HeartbeatMixin):
|
|||
log_and_raise_deployment_error(task, msg, exc=e)
|
||||
|
||||
try:
|
||||
power_state_to_restore = (
|
||||
manager_utils.power_on_node_if_needed(task))
|
||||
task.driver.network.remove_provisioning_network(task)
|
||||
task.driver.network.configure_tenant_networks(task)
|
||||
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
manager_utils.node_power_action(task, states.POWER_ON)
|
||||
except Exception as e:
|
||||
msg = (_('Error rebooting node %(node)s after deploy. '
|
||||
|
|
|
@ -440,7 +440,10 @@ class AnsibleDeploy(agent_base.HeartbeatMixin, base.DeployInterface):
|
|||
def tear_down(self, task):
|
||||
"""Tear down a previous deployment on the task's node."""
|
||||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
power_state_to_restore = manager_utils.power_on_node_if_needed(task)
|
||||
task.driver.network.unconfigure_tenant_networks(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
return states.DELETED
|
||||
|
||||
@METRICS.timer('AnsibleDeploy.prepare')
|
||||
|
@ -451,7 +454,11 @@ class AnsibleDeploy(agent_base.HeartbeatMixin, base.DeployInterface):
|
|||
if node.provision_state == states.DEPLOYING:
|
||||
# adding network-driver dependent provisioning ports
|
||||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
power_state_to_restore = (
|
||||
manager_utils.power_on_node_if_needed(task))
|
||||
task.driver.network.add_provisioning_network(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
if node.provision_state not in [states.ACTIVE, states.ADOPTING]:
|
||||
node.instance_info = deploy_utils.build_instance_info_for_deploy(
|
||||
task)
|
||||
|
@ -534,7 +541,10 @@ class AnsibleDeploy(agent_base.HeartbeatMixin, base.DeployInterface):
|
|||
if not node.driver_internal_info['clean_steps']:
|
||||
# no clean steps configured, nothing to do.
|
||||
return
|
||||
power_state_to_restore = manager_utils.power_on_node_if_needed(task)
|
||||
task.driver.network.add_cleaning_network(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
boot_opt = deploy_utils.build_agent_options(node)
|
||||
task.driver.boot.prepare_ramdisk(task, boot_opt)
|
||||
manager_utils.node_power_action(task, states.REBOOT)
|
||||
|
@ -550,7 +560,10 @@ class AnsibleDeploy(agent_base.HeartbeatMixin, base.DeployInterface):
|
|||
"""
|
||||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
task.driver.boot.clean_up_ramdisk(task)
|
||||
power_state_to_restore = manager_utils.power_on_node_if_needed(task)
|
||||
task.driver.network.remove_cleaning_network(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
|
||||
@METRICS.timer('AnsibleDeploy.continue_deploy')
|
||||
def continue_deploy(self, task):
|
||||
|
@ -622,8 +635,12 @@ class AnsibleDeploy(agent_base.HeartbeatMixin, base.DeployInterface):
|
|||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
else:
|
||||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
power_state_to_restore = (
|
||||
manager_utils.power_on_node_if_needed(task))
|
||||
task.driver.network.remove_provisioning_network(task)
|
||||
task.driver.network.configure_tenant_networks(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
manager_utils.node_power_action(task, states.POWER_ON)
|
||||
except Exception as e:
|
||||
msg = (_('Error rebooting node %(node)s after deploy. '
|
||||
|
|
|
@ -899,7 +899,10 @@ def prepare_inband_cleaning(task, manage_boot=True):
|
|||
:raises: InvalidParameterValue if cleaning network UUID config option has
|
||||
an invalid value.
|
||||
"""
|
||||
power_state_to_restore = manager_utils.power_on_node_if_needed(task)
|
||||
task.driver.network.add_cleaning_network(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
|
||||
# Append required config parameters to node's driver_internal_info
|
||||
# to pass to IPA.
|
||||
|
@ -937,7 +940,10 @@ def tear_down_inband_cleaning(task, manage_boot=True):
|
|||
if manage_boot:
|
||||
task.driver.boot.clean_up_ramdisk(task)
|
||||
|
||||
power_state_to_restore = manager_utils.power_on_node_if_needed(task)
|
||||
task.driver.network.remove_cleaning_network(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
|
||||
|
||||
def get_image_instance_info(node):
|
||||
|
|
|
@ -420,8 +420,12 @@ class ISCSIDeploy(AgentDeployMixin, base.DeployInterface):
|
|||
# This is not being done now as it is expected to be
|
||||
# refactored in the near future.
|
||||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
power_state_to_restore = (
|
||||
manager_utils.power_on_node_if_needed(task))
|
||||
task.driver.network.remove_provisioning_network(task)
|
||||
task.driver.network.configure_tenant_networks(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
task.driver.boot.prepare_instance(task)
|
||||
manager_utils.node_power_action(task, states.POWER_ON)
|
||||
|
||||
|
@ -447,10 +451,13 @@ class ISCSIDeploy(AgentDeployMixin, base.DeployInterface):
|
|||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
task.driver.storage.detach_volumes(task)
|
||||
deploy_utils.tear_down_storage_configuration(task)
|
||||
power_state_to_restore = manager_utils.power_on_node_if_needed(task)
|
||||
task.driver.network.unconfigure_tenant_networks(task)
|
||||
# NOTE(mgoddard): If the deployment was unsuccessful the node may have
|
||||
# ports on the provisioning network which were not deleted.
|
||||
task.driver.network.remove_provisioning_network(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
return states.DELETED
|
||||
|
||||
@METRICS.timer('ISCSIDeploy.prepare')
|
||||
|
@ -485,8 +492,12 @@ class ISCSIDeploy(AgentDeployMixin, base.DeployInterface):
|
|||
# NOTE(vdrok): in case of rebuild, we have tenant network
|
||||
# already configured, unbind tenant ports if present
|
||||
if task.driver.storage.should_write_image(task):
|
||||
power_state_to_restore = (
|
||||
manager_utils.power_on_node_if_needed(task))
|
||||
task.driver.network.unconfigure_tenant_networks(task)
|
||||
task.driver.network.add_provisioning_network(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
task.driver.storage.attach_volumes(task)
|
||||
if not task.driver.storage.should_write_image(task):
|
||||
# We have nothing else to do as this is handled in the
|
||||
|
|
|
@ -266,11 +266,26 @@ def plug_port_to_tenant_network(task, port_like_obj, client=None):
|
|||
if client_id_opt:
|
||||
body['port']['extra_dhcp_opts'] = [client_id_opt]
|
||||
|
||||
is_smart_nic = neutron.is_smartnic_port(port_like_obj)
|
||||
if is_smart_nic:
|
||||
link_info = local_link_info[0]
|
||||
LOG.debug('Setting hostname as host_id in case of Smart NIC, '
|
||||
'port %(port_id)s, hostname %(hostname)s',
|
||||
{'port_id': vif_id,
|
||||
'hostname': link_info['hostname']})
|
||||
body['port']['binding:host_id'] = link_info['hostname']
|
||||
body['port']['binding:vnic_type'] = neutron.VNIC_SMARTNIC
|
||||
|
||||
if not client:
|
||||
client = neutron.get_client(context=task.context)
|
||||
|
||||
if is_smart_nic:
|
||||
neutron.wait_for_host_agent(client, body['port']['binding:host_id'])
|
||||
|
||||
try:
|
||||
client.update_port(vif_id, body)
|
||||
if is_smart_nic:
|
||||
neutron.wait_for_port_status(client, vif_id, 'ACTIVE')
|
||||
except neutron_exceptions.ConnectionFailed as e:
|
||||
msg = (_('Could not add public network VIF %(vif)s '
|
||||
'to node %(node)s, possible network issue. %(exc)s') %
|
||||
|
|
|
@ -258,4 +258,22 @@ class NeutronNetwork(common.NeutronVIFPortIDMixin,
|
|||
or port_like_obj.extra.get('vif_port_id'))
|
||||
if not vif_port_id:
|
||||
continue
|
||||
|
||||
is_smart_nic = neutron.is_smartnic_port(port_like_obj)
|
||||
if is_smart_nic:
|
||||
client = neutron.get_client(context=task.context)
|
||||
link_info = port_like_obj.local_link_connection
|
||||
neutron.wait_for_host_agent(client, link_info['hostname'])
|
||||
|
||||
neutron.unbind_neutron_port(vif_port_id, context=task.context)
|
||||
|
||||
def need_power_on(self, task):
|
||||
"""Check if the node has any Smart NIC ports
|
||||
|
||||
:param task: A TaskManager instance.
|
||||
:return: A boolean to indicate Smart NIC port presence
|
||||
"""
|
||||
for port in task.ports:
|
||||
if neutron.is_smartnic_port(port):
|
||||
return True
|
||||
return False
|
||||
|
|
|
@ -339,7 +339,10 @@ class PXERamdiskDeploy(agent.AgentDeploy):
|
|||
# IDEA(TheJulia): Maybe a "trusted environment" mode flag
|
||||
# that we otherwise fail validation on for drivers that
|
||||
# require explicit security postures.
|
||||
power_state_to_restore = manager_utils.power_on_node_if_needed(task)
|
||||
task.driver.network.configure_tenant_networks(task)
|
||||
manager_utils.restore_power_state_if_needed(
|
||||
task, power_state_to_restore)
|
||||
|
||||
# calling boot.prepare_instance will also set the node
|
||||
# to PXE boot, and update PXE templates accordingly
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import time
|
||||
|
||||
from keystoneauth1 import loading as kaloading
|
||||
import mock
|
||||
from neutronclient.common import exceptions as neutron_client_exc
|
||||
|
@ -185,6 +187,8 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
|
|||
'mac_address': '52:54:00:cf:2d:32'}
|
||||
self.network_uuid = uuidutils.generate_uuid()
|
||||
self.client_mock = mock.Mock()
|
||||
self.client_mock.list_agents.return_value = {
|
||||
'agents': [{'alive': True}]}
|
||||
patcher = mock.patch('ironic.common.neutron.get_client',
|
||||
return_value=self.client_mock, autospec=True)
|
||||
patcher.start()
|
||||
|
@ -582,6 +586,120 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
|
|||
self.assertTrue(res)
|
||||
self.assertFalse(log_mock.warning.called)
|
||||
|
||||
def test_validate_agent_up(self):
|
||||
self.client_mock.list_agents.return_value = {
|
||||
'agents': [{'alive': True}]}
|
||||
self.assertTrue(neutron.validate_agent(self.client_mock))
|
||||
|
||||
def test_validate_agent_down(self):
|
||||
self.client_mock.list_agents.return_value = {
|
||||
'agents': [{'alive': False}]}
|
||||
self.assertFalse(neutron.validate_agent(self.client_mock))
|
||||
|
||||
def test_is_smartnic_port_true(self):
|
||||
port = self.ports[0]
|
||||
port.is_smartnic = True
|
||||
self.assertTrue(neutron.is_smartnic_port(port))
|
||||
|
||||
def test_is_smartnic_port_false(self):
|
||||
port = self.ports[0]
|
||||
self.assertFalse(neutron.is_smartnic_port(port))
|
||||
|
||||
@mock.patch.object(neutron, 'validate_agent')
|
||||
@mock.patch.object(time, 'sleep')
|
||||
def test_wait_for_host_agent_up(self, sleep_mock, validate_agent_mock):
|
||||
validate_agent_mock.return_value = True
|
||||
neutron.wait_for_host_agent(self.client_mock, 'hostname')
|
||||
sleep_mock.assert_not_called()
|
||||
|
||||
@mock.patch.object(neutron, 'validate_agent')
|
||||
@mock.patch.object(time, 'sleep')
|
||||
def test_wait_for_host_agent_down(self, sleep_mock, validate_agent_mock):
|
||||
validate_agent_mock.side_effect = [False, True]
|
||||
neutron.wait_for_host_agent(self.client_mock, 'hostname')
|
||||
sleep_mock.assert_called_once()
|
||||
|
||||
@mock.patch.object(neutron, '_get_port_by_uuid')
|
||||
@mock.patch.object(time, 'sleep')
|
||||
def test_wait_for_port_status_up(self, sleep_mock, get_port_mock):
|
||||
get_port_mock.return_value = {'status': 'ACTIVE'}
|
||||
neutron.wait_for_port_status(self.client_mock, 'port_id', 'ACTIVE')
|
||||
sleep_mock.assert_not_called()
|
||||
|
||||
@mock.patch.object(neutron, '_get_port_by_uuid')
|
||||
@mock.patch.object(time, 'sleep')
|
||||
def test_wait_for_port_status_down(self, sleep_mock, get_port_mock):
|
||||
get_port_mock.side_effect = [{'status': 'DOWN'}, {'status': 'ACTIVE'}]
|
||||
neutron.wait_for_port_status(self.client_mock, 'port_id', 'ACTIVE')
|
||||
sleep_mock.assert_called_once()
|
||||
|
||||
@mock.patch.object(neutron, 'wait_for_host_agent', autospec=True)
|
||||
@mock.patch.object(neutron, 'wait_for_port_status', autospec=True)
|
||||
def test_add_smartnic_port_to_network(
|
||||
self, wait_port_mock, wait_agent_mock):
|
||||
# Ports will be created only if pxe_enabled is True
|
||||
self.node.network_interface = 'neutron'
|
||||
self.node.save()
|
||||
object_utils.create_test_port(
|
||||
self.context, node_id=self.node.id,
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
address='52:54:00:cf:2d:22',
|
||||
pxe_enabled=False
|
||||
)
|
||||
port = self.ports[0]
|
||||
|
||||
local_link_connection = port.local_link_connection
|
||||
local_link_connection['hostname'] = 'hostname'
|
||||
port.local_link_connection = local_link_connection
|
||||
port.is_smartnic = True
|
||||
port.save()
|
||||
|
||||
expected_body = {
|
||||
'port': {
|
||||
'network_id': self.network_uuid,
|
||||
'admin_state_up': True,
|
||||
'binding:vnic_type': 'smart-nic',
|
||||
'device_owner': 'baremetal:none',
|
||||
'binding:host_id': port.local_link_connection['hostname'],
|
||||
'device_id': self.node.uuid,
|
||||
'mac_address': port.address,
|
||||
'binding:profile': {
|
||||
'local_link_information': [port.local_link_connection]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Ensure we can create ports
|
||||
self.client_mock.create_port.return_value = {
|
||||
'port': self.neutron_port}
|
||||
expected = {port.uuid: self.neutron_port['id']}
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
ports = neutron.add_ports_to_network(task, self.network_uuid)
|
||||
self.assertEqual(expected, ports)
|
||||
self.client_mock.create_port.assert_called_once_with(
|
||||
expected_body)
|
||||
wait_agent_mock.assert_called_once_with(
|
||||
self.client_mock, 'hostname')
|
||||
wait_port_mock.assert_called_once_with(
|
||||
self.client_mock, self.neutron_port['id'], 'ACTIVE')
|
||||
|
||||
@mock.patch.object(neutron, 'is_smartnic_port', autospec=True)
|
||||
@mock.patch.object(neutron, 'wait_for_host_agent', autospec=True)
|
||||
def test_remove_neutron_smartnic_ports(
|
||||
self, wait_agent_mock, is_smartnic_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
is_smartnic_mock.return_value = True
|
||||
self.neutron_port['binding:host_id'] = 'hostname'
|
||||
self.client_mock.list_ports.return_value = {
|
||||
'ports': [self.neutron_port]}
|
||||
neutron.remove_neutron_ports(task, {'param': 'value'})
|
||||
self.client_mock.list_ports.assert_called_once_with(
|
||||
**{'param': 'value'})
|
||||
self.client_mock.delete_port.assert_called_once_with(
|
||||
self.neutron_port['id'])
|
||||
is_smartnic_mock.assert_called_once_with(self.neutron_port)
|
||||
wait_agent_mock.assert_called_once_with(self.client_mock, 'hostname')
|
||||
|
||||
|
||||
@mock.patch.object(neutron, 'get_client', autospec=True)
|
||||
class TestValidateNetwork(base.TestCase):
|
||||
|
|
|
@ -9,11 +9,13 @@
|
|||
# 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 time
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import boot_modes
|
||||
from ironic.common import exception
|
||||
from ironic.common import network
|
||||
|
@ -2011,6 +2013,86 @@ class MiscTestCase(db_base.DbTestCase):
|
|||
mock_resume.assert_called_once_with(
|
||||
task, 'deploying', 'continue_node_deploy')
|
||||
|
||||
@mock.patch.object(time, 'sleep', autospec=True)
|
||||
@mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
|
||||
@mock.patch.object(drivers_base.NetworkInterface, 'need_power_on')
|
||||
@mock.patch.object(conductor_utils, 'node_set_boot_device',
|
||||
autospec=True)
|
||||
@mock.patch.object(conductor_utils, 'node_power_action',
|
||||
autospec=True)
|
||||
def test_power_on_node_if_needed_true(
|
||||
self, power_action_mock, boot_device_mock,
|
||||
need_power_on_mock, get_power_state_mock, time_mock):
|
||||
with task_manager.acquire(
|
||||
self.context, self.node.uuid, shared=False) as task:
|
||||
need_power_on_mock.return_value = True
|
||||
get_power_state_mock.return_value = states.POWER_OFF
|
||||
power_state = conductor_utils.power_on_node_if_needed(task)
|
||||
self.assertEqual(power_state, states.POWER_OFF)
|
||||
boot_device_mock.assert_called_once_with(
|
||||
task, boot_devices.BIOS, persistent=False)
|
||||
power_action_mock.assert_called_once_with(task, states.POWER_ON)
|
||||
|
||||
@mock.patch.object(time, 'sleep', autospec=True)
|
||||
@mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
|
||||
@mock.patch.object(drivers_base.NetworkInterface, 'need_power_on')
|
||||
@mock.patch.object(conductor_utils, 'node_set_boot_device',
|
||||
autospec=True)
|
||||
@mock.patch.object(conductor_utils, 'node_power_action',
|
||||
autospec=True)
|
||||
def test_power_on_node_if_needed_false_power_on(
|
||||
self, power_action_mock, boot_device_mock,
|
||||
need_power_on_mock, get_power_state_mock, time_mock):
|
||||
with task_manager.acquire(
|
||||
self.context, self.node.uuid, shared=False) as task:
|
||||
need_power_on_mock.return_value = True
|
||||
get_power_state_mock.return_value = states.POWER_ON
|
||||
power_state = conductor_utils.power_on_node_if_needed(task)
|
||||
self.assertIsNone(power_state)
|
||||
self.assertEqual(0, boot_device_mock.call_count)
|
||||
self.assertEqual(0, power_action_mock.call_count)
|
||||
|
||||
@mock.patch.object(time, 'sleep', autospec=True)
|
||||
@mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
|
||||
@mock.patch.object(drivers_base.NetworkInterface, 'need_power_on')
|
||||
@mock.patch.object(conductor_utils, 'node_set_boot_device',
|
||||
autospec=True)
|
||||
@mock.patch.object(conductor_utils, 'node_power_action',
|
||||
autospec=True)
|
||||
def test_power_on_node_if_needed_false_no_need(
|
||||
self, power_action_mock, boot_device_mock,
|
||||
need_power_on_mock, get_power_state_mock, time_mock):
|
||||
with task_manager.acquire(
|
||||
self.context, self.node.uuid, shared=False) as task:
|
||||
need_power_on_mock.return_value = False
|
||||
get_power_state_mock.return_value = states.POWER_OFF
|
||||
power_state = conductor_utils.power_on_node_if_needed(task)
|
||||
self.assertIsNone(power_state)
|
||||
self.assertEqual(0, boot_device_mock.call_count)
|
||||
self.assertEqual(0, power_action_mock.call_count)
|
||||
|
||||
@mock.patch.object(time, 'sleep', autospec=True)
|
||||
@mock.patch.object(conductor_utils, 'node_power_action',
|
||||
autospec=True)
|
||||
def test_restore_power_state_if_needed_true(
|
||||
self, power_action_mock, time_mock):
|
||||
with task_manager.acquire(
|
||||
self.context, self.node.uuid, shared=False) as task:
|
||||
power_state = states.POWER_OFF
|
||||
conductor_utils.restore_power_state_if_needed(task, power_state)
|
||||
power_action_mock.assert_called_once_with(task, power_state)
|
||||
|
||||
@mock.patch.object(time, 'sleep', autospec=True)
|
||||
@mock.patch.object(conductor_utils, 'node_power_action',
|
||||
autospec=True)
|
||||
def test_restore_power_state_if_needed_false(
|
||||
self, power_action_mock, time_mock):
|
||||
with task_manager.acquire(
|
||||
self.context, self.node.uuid, shared=False) as task:
|
||||
power_state = None
|
||||
conductor_utils.restore_power_state_if_needed(task, power_state)
|
||||
self.assertEqual(0, power_action_mock.call_count)
|
||||
|
||||
|
||||
class ValidateInstanceInfoTraitsTestCase(tests_base.TestCase):
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ from ironic.conductor import utils
|
|||
from ironic.drivers.modules.ansible import deploy as ansible_deploy
|
||||
from ironic.drivers.modules import deploy_utils
|
||||
from ironic.drivers.modules import fake
|
||||
from ironic.drivers.modules.network import flat as flat_network
|
||||
from ironic.drivers.modules import pxe
|
||||
from ironic.tests.unit.db import base as db_base
|
||||
from ironic.tests.unit.objects import utils as object_utils
|
||||
|
@ -792,11 +793,13 @@ class TestAnsibleDeploy(AnsibleDeployTestCaseBase):
|
|||
run_playbook_mock.assert_called_once_with(
|
||||
task.node, 'test_pl', ironic_nodes, 'test_k')
|
||||
|
||||
@mock.patch.object(utils, 'power_on_node_if_needed', autospec=True)
|
||||
@mock.patch.object(fake.FakePower, 'get_power_state',
|
||||
return_value=states.POWER_OFF)
|
||||
@mock.patch.object(utils, 'node_power_action', autospec=True)
|
||||
def test_reboot_and_finish_deploy_force_reboot(self, power_action_mock,
|
||||
get_pow_state_mock):
|
||||
def test_reboot_and_finish_deploy_force_reboot(
|
||||
self, power_action_mock, get_pow_state_mock,
|
||||
power_on_node_if_needed_mock):
|
||||
d_info = self.node.driver_info
|
||||
d_info['deploy_forces_oob_reboot'] = True
|
||||
self.node.driver_info = d_info
|
||||
|
@ -806,6 +809,7 @@ class TestAnsibleDeploy(AnsibleDeployTestCaseBase):
|
|||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.save()
|
||||
|
||||
power_on_node_if_needed_mock.return_value = None
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
with mock.patch.object(task.driver, 'network') as net_mock:
|
||||
self.driver.reboot_and_finish_deploy(task)
|
||||
|
@ -819,11 +823,12 @@ class TestAnsibleDeploy(AnsibleDeployTestCaseBase):
|
|||
power_action_mock.call_args_list)
|
||||
get_pow_state_mock.assert_not_called()
|
||||
|
||||
@mock.patch.object(utils, 'power_on_node_if_needed', autospec=True)
|
||||
@mock.patch.object(ansible_deploy, '_run_playbook', autospec=True)
|
||||
@mock.patch.object(utils, 'node_power_action', autospec=True)
|
||||
def test_reboot_and_finish_deploy_soft_poweroff_retry(self,
|
||||
power_action_mock,
|
||||
run_playbook_mock):
|
||||
def test_reboot_and_finish_deploy_soft_poweroff_retry(
|
||||
self, power_action_mock, run_playbook_mock,
|
||||
power_on_node_if_needed_mock):
|
||||
self.config(group='ansible',
|
||||
post_deploy_get_power_state_retry_interval=0)
|
||||
self.config(group='ansible',
|
||||
|
@ -834,6 +839,7 @@ class TestAnsibleDeploy(AnsibleDeployTestCaseBase):
|
|||
self.node.driver_internal_info = di_info
|
||||
self.node.save()
|
||||
|
||||
power_on_node_if_needed_mock.return_value = None
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
with mock.patch.object(task.driver, 'network') as net_mock:
|
||||
with mock.patch.object(task.driver.power,
|
||||
|
@ -916,3 +922,141 @@ class TestAnsibleDeploy(AnsibleDeployTestCaseBase):
|
|||
task)
|
||||
task.driver.boot.clean_up_ramdisk.assert_called_once_with(
|
||||
task)
|
||||
|
||||
@mock.patch.object(utils, 'restore_power_state_if_needed', autospec=True)
|
||||
@mock.patch.object(utils, 'power_on_node_if_needed')
|
||||
@mock.patch.object(utils, 'node_power_action', autospec=True)
|
||||
def test_tear_down_with_smartnic_port(
|
||||
self, power_mock, power_on_node_if_needed_mock,
|
||||
restore_power_state_mock):
|
||||
with task_manager.acquire(
|
||||
self.context, self.node['uuid'], shared=False) as task:
|
||||
power_on_node_if_needed_mock.return_value = states.POWER_OFF
|
||||
driver_return = self.driver.tear_down(task)
|
||||
power_mock.assert_called_once_with(task, states.POWER_OFF)
|
||||
self.assertEqual(driver_return, states.DELETED)
|
||||
power_on_node_if_needed_mock.assert_called_once_with(task)
|
||||
restore_power_state_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
|
||||
@mock.patch.object(flat_network.FlatNetwork, 'add_provisioning_network',
|
||||
autospec=True)
|
||||
@mock.patch.object(utils, 'restore_power_state_if_needed', autospec=True)
|
||||
@mock.patch.object(utils, 'power_on_node_if_needed', autospec=True)
|
||||
@mock.patch.object(utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'build_agent_options', autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'build_instance_info_for_deploy',
|
||||
autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_ramdisk')
|
||||
def test_prepare_with_smartnic_port(
|
||||
self, pxe_prepare_ramdisk_mock,
|
||||
build_instance_info_mock, build_options_mock,
|
||||
power_action_mock, power_on_node_if_needed_mock,
|
||||
restore_power_state_mock, net_mock):
|
||||
with task_manager.acquire(
|
||||
self.context, self.node['uuid'], shared=False) as task:
|
||||
task.node.provision_state = states.DEPLOYING
|
||||
build_instance_info_mock.return_value = {'test': 'test'}
|
||||
build_options_mock.return_value = {'op1': 'test1'}
|
||||
power_on_node_if_needed_mock.return_value = states.POWER_OFF
|
||||
self.driver.prepare(task)
|
||||
power_action_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
build_instance_info_mock.assert_called_once_with(task)
|
||||
build_options_mock.assert_called_once_with(task.node)
|
||||
pxe_prepare_ramdisk_mock.assert_called_once_with(
|
||||
task, {'op1': 'test1'})
|
||||
power_on_node_if_needed_mock.assert_called_once_with(task)
|
||||
restore_power_state_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
|
||||
self.node.refresh()
|
||||
self.assertEqual('test', self.node.instance_info['test'])
|
||||
|
||||
@mock.patch.object(utils, 'restore_power_state_if_needed', autospec=True)
|
||||
@mock.patch.object(utils, 'power_on_node_if_needed', autospec=True)
|
||||
@mock.patch.object(ansible_deploy, '_run_playbook', autospec=True)
|
||||
@mock.patch.object(utils, 'set_node_cleaning_steps', autospec=True)
|
||||
@mock.patch.object(utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'build_agent_options', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_ramdisk')
|
||||
def test_prepare_cleaning_with_smartnic_port(
|
||||
self, prepare_ramdisk_mock, build_options_mock, power_action_mock,
|
||||
set_node_cleaning_steps, run_playbook_mock,
|
||||
power_on_node_if_needed_mock, restore_power_state_mock):
|
||||
step = {'priority': 10, 'interface': 'deploy',
|
||||
'step': 'erase_devices', 'tags': ['clean']}
|
||||
driver_internal_info = dict(DRIVER_INTERNAL_INFO)
|
||||
driver_internal_info['clean_steps'] = [step]
|
||||
self.node.driver_internal_info = driver_internal_info
|
||||
self.node.save()
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.network.add_cleaning_network = mock.Mock()
|
||||
build_options_mock.return_value = {'op1': 'test1'}
|
||||
power_on_node_if_needed_mock.return_value = states.POWER_OFF
|
||||
state = self.driver.prepare_cleaning(task)
|
||||
set_node_cleaning_steps.assert_called_once_with(task)
|
||||
task.driver.network.add_cleaning_network.assert_called_once_with(
|
||||
task)
|
||||
build_options_mock.assert_called_once_with(task.node)
|
||||
prepare_ramdisk_mock.assert_called_once_with(
|
||||
task, {'op1': 'test1'})
|
||||
power_action_mock.assert_called_once_with(task, states.REBOOT)
|
||||
self.assertFalse(run_playbook_mock.called)
|
||||
self.assertEqual(states.CLEANWAIT, state)
|
||||
power_on_node_if_needed_mock.assert_called_once_with(task)
|
||||
restore_power_state_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
|
||||
@mock.patch.object(utils, 'restore_power_state_if_needed', autospec=True)
|
||||
@mock.patch.object(utils, 'power_on_node_if_needed', autospec=True)
|
||||
@mock.patch.object(utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'clean_up_ramdisk')
|
||||
def test_tear_down_cleaning_with_smartnic_port(
|
||||
self, clean_ramdisk_mock, power_action_mock,
|
||||
power_on_node_if_needed_mock, restore_power_state_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.network.remove_cleaning_network = mock.Mock()
|
||||
power_on_node_if_needed_mock.return_value = states.POWER_OFF
|
||||
self.driver.tear_down_cleaning(task)
|
||||
power_action_mock.assert_called_once_with(task, states.POWER_OFF)
|
||||
clean_ramdisk_mock.assert_called_once_with(task)
|
||||
(task.driver.network.remove_cleaning_network
|
||||
.assert_called_once_with(task))
|
||||
power_on_node_if_needed_mock.assert_called_once_with(task)
|
||||
restore_power_state_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
|
||||
@mock.patch.object(flat_network.FlatNetwork, 'remove_provisioning_network',
|
||||
autospec=True)
|
||||
@mock.patch.object(flat_network.FlatNetwork, 'configure_tenant_networks',
|
||||
autospec=True)
|
||||
@mock.patch.object(utils, 'restore_power_state_if_needed', autospec=True)
|
||||
@mock.patch.object(utils, 'power_on_node_if_needed', autospec=True)
|
||||
@mock.patch.object(fake.FakePower, 'get_power_state',
|
||||
return_value=states.POWER_OFF)
|
||||
@mock.patch.object(utils, 'node_power_action', autospec=True)
|
||||
def test_reboot_and_finish_deploy_with_smartnic_port(
|
||||
self, power_action_mock, get_pow_state_mock,
|
||||
power_on_node_if_needed_mock, restore_power_state_mock,
|
||||
configure_tenant_networks_mock, remove_provisioning_network_mock):
|
||||
d_info = self.node.driver_info
|
||||
d_info['deploy_forces_oob_reboot'] = True
|
||||
self.node.driver_info = d_info
|
||||
self.node.save()
|
||||
self.config(group='ansible',
|
||||
post_deploy_get_power_state_retry_interval=0)
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.save()
|
||||
power_on_node_if_needed_mock.return_value = states.POWER_OFF
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.driver.reboot_and_finish_deploy(task)
|
||||
expected_power_calls = [((task, states.POWER_OFF),),
|
||||
((task, states.POWER_ON),)]
|
||||
self.assertEqual(
|
||||
expected_power_calls, power_action_mock.call_args_list)
|
||||
power_on_node_if_needed_mock.assert_called_once_with(task)
|
||||
restore_power_state_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
get_pow_state_mock.assert_not_called()
|
||||
|
|
|
@ -430,6 +430,26 @@ class TestCommonFunctions(db_base.DbTestCase):
|
|||
common.plug_port_to_tenant_network,
|
||||
task, self.port)
|
||||
|
||||
@mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True)
|
||||
@mock.patch.object(neutron_common, 'wait_for_port_status', autospec=True)
|
||||
@mock.patch.object(neutron_common, 'get_client', autospec=True)
|
||||
def test_plug_port_to_tenant_network_smartnic_port(
|
||||
self, mock_gc, wait_port_mock, wait_agent_mock):
|
||||
nclient = mock.MagicMock()
|
||||
mock_gc.return_value = nclient
|
||||
local_link_connection = self.port.local_link_connection
|
||||
local_link_connection['hostname'] = 'hostname'
|
||||
self.port.local_link_connection = local_link_connection
|
||||
self.port.internal_info = {common.TENANT_VIF_KEY: self.vif_id}
|
||||
self.port.is_smartnic = True
|
||||
self.port.save()
|
||||
with task_manager.acquire(self.context, self.node.id) as task:
|
||||
common.plug_port_to_tenant_network(task, self.port)
|
||||
wait_agent_mock.assert_called_once_with(
|
||||
nclient, 'hostname')
|
||||
wait_port_mock.assert_called_once_with(
|
||||
nclient, self.vif_id, 'ACTIVE')
|
||||
|
||||
|
||||
class TestVifPortIDMixin(db_base.DbTestCase):
|
||||
|
||||
|
|
|
@ -545,6 +545,24 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
|
|||
mock_unbind_port.assert_called_once_with(
|
||||
self.port.extra['vif_port_id'], context=task.context)
|
||||
|
||||
@mock.patch.object(neutron_common, 'get_client')
|
||||
@mock.patch.object(neutron_common, 'wait_for_host_agent')
|
||||
@mock.patch.object(neutron_common, 'unbind_neutron_port')
|
||||
def test_unconfigure_tenant_networks_smartnic(
|
||||
self, mock_unbind_port, wait_agent_mock, client_mock):
|
||||
nclient = mock.MagicMock()
|
||||
client_mock.return_value = nclient
|
||||
local_link_connection = self.port.local_link_connection
|
||||
local_link_connection['hostname'] = 'hostname'
|
||||
self.port.local_link_connection = local_link_connection
|
||||
self.port.is_smartnic = True
|
||||
self.port.save()
|
||||
with task_manager.acquire(self.context, self.node.id) as task:
|
||||
self.interface.unconfigure_tenant_networks(task)
|
||||
mock_unbind_port.assert_called_once_with(
|
||||
self.port.extra['vif_port_id'], context=task.context)
|
||||
wait_agent_mock.assert_called_once_with(nclient, 'hostname')
|
||||
|
||||
def test_configure_tenant_networks_no_ports_for_node(self):
|
||||
n = utils.create_test_node(self.context, network_interface='neutron',
|
||||
uuid=uuidutils.generate_uuid())
|
||||
|
@ -571,10 +589,11 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
|
|||
self.assertIn('No neutron ports or portgroups are associated with',
|
||||
log_mock.error.call_args[0][0])
|
||||
|
||||
@mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True)
|
||||
@mock.patch.object(neutron_common, 'get_client')
|
||||
@mock.patch.object(neutron, 'LOG')
|
||||
def test_configure_tenant_networks_multiple_ports_one_vif_id(
|
||||
self, log_mock, client_mock):
|
||||
self, log_mock, client_mock, wait_agent_mock):
|
||||
expected_body = {
|
||||
'port': {
|
||||
'binding:vnic_type': 'baremetal',
|
||||
|
@ -592,8 +611,10 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
|
|||
upd_mock.assert_called_once_with(self.port.extra['vif_port_id'],
|
||||
expected_body)
|
||||
|
||||
@mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True)
|
||||
@mock.patch.object(neutron_common, 'get_client')
|
||||
def test_configure_tenant_networks_update_fail(self, client_mock):
|
||||
def test_configure_tenant_networks_update_fail(self, client_mock,
|
||||
wait_agent_mock):
|
||||
client = client_mock.return_value
|
||||
client.update_port.side_effect = neutron_exceptions.ConnectionFailed(
|
||||
reason='meow')
|
||||
|
@ -603,8 +624,10 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
|
|||
self.interface.configure_tenant_networks, task)
|
||||
client_mock.assert_called_once_with(context=task.context)
|
||||
|
||||
@mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True)
|
||||
@mock.patch.object(neutron_common, 'get_client')
|
||||
def _test_configure_tenant_networks(self, client_mock, is_client_id=False,
|
||||
def _test_configure_tenant_networks(self, client_mock, wait_agent_mock,
|
||||
is_client_id=False,
|
||||
vif_int_info=False):
|
||||
upd_mock = mock.Mock()
|
||||
client_mock.return_value.update_port = upd_mock
|
||||
|
@ -687,11 +710,12 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
|
|||
self.node.save()
|
||||
self._test_configure_tenant_networks(is_client_id=True)
|
||||
|
||||
@mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True)
|
||||
@mock.patch.object(neutron_common, 'get_client', autospec=True)
|
||||
@mock.patch.object(neutron_common, 'get_local_group_information',
|
||||
autospec=True)
|
||||
def test_configure_tenant_networks_with_portgroups(
|
||||
self, glgi_mock, client_mock):
|
||||
self, glgi_mock, client_mock, wait_agent_mock):
|
||||
pg = utils.create_test_portgroup(
|
||||
self.context, node_id=self.node.id, address='ff:54:00:cf:2d:32',
|
||||
extra={'vif_port_id': uuidutils.generate_uuid()})
|
||||
|
@ -745,3 +769,13 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
|
|||
[mock.call(self.port.extra['vif_port_id'], call1_body),
|
||||
mock.call(pg.extra['vif_port_id'], call2_body)]
|
||||
)
|
||||
|
||||
def test_need_power_on_true(self):
|
||||
self.port.is_smartnic = True
|
||||
self.port.save()
|
||||
with task_manager.acquire(self.context, self.node.id) as task:
|
||||
self.assertTrue(self.interface.need_power_on(task))
|
||||
|
||||
def test_need_power_on_false(self):
|
||||
with task_manager.acquire(self.context, self.node.id) as task:
|
||||
self.assertFalse(self.interface.need_power_on(task))
|
||||
|
|
|
@ -1001,6 +1001,8 @@ class TestAgentDeploy(db_base.DbTestCase):
|
|||
self.assertEqual(states.ACTIVE,
|
||||
task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'remove_http_instance_symlink',
|
||||
autospec=True)
|
||||
@mock.patch.object(agent.LOG, 'warning', spec_set=True, autospec=True)
|
||||
|
@ -1018,7 +1020,8 @@ class TestAgentDeploy(db_base.DbTestCase):
|
|||
def test_reboot_to_instance(self, check_deploy_mock,
|
||||
prepare_instance_mock, power_off_mock,
|
||||
get_power_state_mock, node_power_action_mock,
|
||||
uuid_mock, log_mock, remove_symlink_mock):
|
||||
uuid_mock, log_mock, remove_symlink_mock,
|
||||
power_on_node_if_needed_mock):
|
||||
self.config(manage_agent_boot=True, group='agent')
|
||||
self.config(image_download_source='http', group='agent')
|
||||
check_deploy_mock.return_value = None
|
||||
|
@ -1029,6 +1032,7 @@ class TestAgentDeploy(db_base.DbTestCase):
|
|||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
get_power_state_mock.return_value = states.POWER_OFF
|
||||
power_on_node_if_needed_mock.return_value = None
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = True
|
||||
task.driver.deploy.reboot_to_instance(task)
|
||||
check_deploy_mock.assert_called_once_with(mock.ANY, task.node)
|
||||
|
@ -1048,6 +1052,8 @@ class TestAgentDeploy(db_base.DbTestCase):
|
|||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
self.assertTrue(remove_symlink_mock.called)
|
||||
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(agent.LOG, 'warning', spec_set=True, autospec=True)
|
||||
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
||||
@mock.patch.object(agent.AgentDeployMixin, '_get_uuid_from_result',
|
||||
|
@ -1061,13 +1067,10 @@ class TestAgentDeploy(db_base.DbTestCase):
|
|||
autospec=True)
|
||||
@mock.patch('ironic.drivers.modules.agent.AgentDeployMixin'
|
||||
'.check_deploy_success', autospec=True)
|
||||
def test_reboot_to_instance_no_manage_agent_boot(self, check_deploy_mock,
|
||||
prepare_instance_mock,
|
||||
power_off_mock,
|
||||
get_power_state_mock,
|
||||
node_power_action_mock,
|
||||
uuid_mock, bootdev_mock,
|
||||
log_mock):
|
||||
def test_reboot_to_instance_no_manage_agent_boot(
|
||||
self, check_deploy_mock, prepare_instance_mock, power_off_mock,
|
||||
get_power_state_mock, node_power_action_mock, uuid_mock,
|
||||
bootdev_mock, log_mock, power_on_node_if_needed_mock):
|
||||
self.config(manage_agent_boot=False, group='agent')
|
||||
check_deploy_mock.return_value = None
|
||||
uuid_mock.return_value = None
|
||||
|
@ -1076,6 +1079,7 @@ class TestAgentDeploy(db_base.DbTestCase):
|
|||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
power_on_node_if_needed_mock.return_value = None
|
||||
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)
|
||||
|
@ -1093,6 +1097,8 @@ class TestAgentDeploy(db_base.DbTestCase):
|
|||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(agent.LOG, 'warning', spec_set=True, autospec=True)
|
||||
@mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy',
|
||||
autospec=True)
|
||||
|
@ -1113,7 +1119,8 @@ class TestAgentDeploy(db_base.DbTestCase):
|
|||
get_power_state_mock,
|
||||
node_power_action_mock,
|
||||
uuid_mock, boot_mode_mock,
|
||||
log_mock):
|
||||
log_mock,
|
||||
power_on_node_if_needed_mock):
|
||||
check_deploy_mock.return_value = None
|
||||
uuid_mock.return_value = 'root_uuid'
|
||||
self.node.provision_state = states.DEPLOYWAIT
|
||||
|
@ -1122,6 +1129,7 @@ class TestAgentDeploy(db_base.DbTestCase):
|
|||
boot_mode_mock.return_value = 'bios'
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
power_on_node_if_needed_mock.return_value = None
|
||||
get_power_state_mock.return_value = states.POWER_OFF
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
driver_internal_info['is_whole_disk_image'] = False
|
||||
|
@ -1146,6 +1154,8 @@ class TestAgentDeploy(db_base.DbTestCase):
|
|||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(agent.LOG, 'warning', spec_set=True, autospec=True)
|
||||
@mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy',
|
||||
autospec=True)
|
||||
|
@ -1163,7 +1173,8 @@ class TestAgentDeploy(db_base.DbTestCase):
|
|||
def test_reboot_to_instance_partition_localboot_ppc64(
|
||||
self, check_deploy_mock, prepare_instance_mock,
|
||||
power_off_mock, get_power_state_mock,
|
||||
node_power_action_mock, uuid_mock, boot_mode_mock, log_mock):
|
||||
node_power_action_mock, uuid_mock, boot_mode_mock, log_mock,
|
||||
power_on_node_if_needed_mock):
|
||||
check_deploy_mock.return_value = None
|
||||
uuid_mock.side_effect = ['root_uuid', 'prep_boot_part_uuid']
|
||||
self.node.provision_state = states.DEPLOYWAIT
|
||||
|
@ -1172,6 +1183,7 @@ class TestAgentDeploy(db_base.DbTestCase):
|
|||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
power_on_node_if_needed_mock.return_value = None
|
||||
get_power_state_mock.return_value = states.POWER_OFF
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
driver_internal_info['is_whole_disk_image'] = False
|
||||
|
@ -1238,6 +1250,8 @@ class TestAgentDeploy(db_base.DbTestCase):
|
|||
self.assertEqual(states.DEPLOYFAIL, task.node.provision_state)
|
||||
self.assertEqual(states.ACTIVE, task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(agent.LOG, 'warning', spec_set=True, autospec=True)
|
||||
@mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy',
|
||||
autospec=True)
|
||||
|
@ -1258,7 +1272,8 @@ class TestAgentDeploy(db_base.DbTestCase):
|
|||
get_power_state_mock,
|
||||
node_power_action_mock,
|
||||
uuid_mock, boot_mode_mock,
|
||||
log_mock):
|
||||
log_mock,
|
||||
power_on_node_if_needed_mock):
|
||||
check_deploy_mock.return_value = None
|
||||
uuid_mock.side_effect = ['root_uuid', 'efi_uuid']
|
||||
self.node.provision_state = states.DEPLOYWAIT
|
||||
|
@ -1267,6 +1282,7 @@ class TestAgentDeploy(db_base.DbTestCase):
|
|||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
power_on_node_if_needed_mock.return_value = None
|
||||
get_power_state_mock.return_value = states.POWER_OFF
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
driver_internal_info['is_whole_disk_image'] = False
|
||||
|
@ -1367,6 +1383,125 @@ class TestAgentDeploy(db_base.DbTestCase):
|
|||
'command_status': 'RUNNING'}]
|
||||
self.assertFalse(task.driver.deploy.deploy_is_done(task))
|
||||
|
||||
@mock.patch.object(manager_utils, 'restore_power_state_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(noop_storage.NoopStorage, 'attach_volumes',
|
||||
autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'populate_storage_driver_internal_info')
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_ramdisk')
|
||||
@mock.patch.object(deploy_utils, 'build_agent_options')
|
||||
@mock.patch.object(deploy_utils, 'build_instance_info_for_deploy')
|
||||
@mock.patch.object(flat_network.FlatNetwork,
|
||||
'add_provisioning_network',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(flat_network.FlatNetwork,
|
||||
'unconfigure_tenant_networks',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(flat_network.FlatNetwork, 'validate',
|
||||
spec_set=True, autospec=True)
|
||||
def test_prepare_with_smartnic_port(
|
||||
self, validate_net_mock,
|
||||
unconfigure_tenant_net_mock, add_provisioning_net_mock,
|
||||
build_instance_info_mock, build_options_mock,
|
||||
pxe_prepare_ramdisk_mock, storage_driver_info_mock,
|
||||
storage_attach_volumes_mock, power_on_node_if_needed_mock,
|
||||
restore_power_state_mock):
|
||||
node = self.node
|
||||
node.network_interface = 'flat'
|
||||
node.save()
|
||||
add_provisioning_net_mock.return_value = None
|
||||
with task_manager.acquire(
|
||||
self.context, self.node['uuid'], shared=False) as task:
|
||||
task.node.provision_state = states.DEPLOYING
|
||||
build_instance_info_mock.return_value = {'foo': 'bar'}
|
||||
build_options_mock.return_value = {'a': 'b'}
|
||||
power_on_node_if_needed_mock.return_value = states.POWER_OFF
|
||||
self.driver.prepare(task)
|
||||
storage_driver_info_mock.assert_called_once_with(task)
|
||||
validate_net_mock.assert_called_once_with(mock.ANY, task)
|
||||
add_provisioning_net_mock.assert_called_once_with(mock.ANY, task)
|
||||
unconfigure_tenant_net_mock.assert_called_once_with(mock.ANY, task)
|
||||
storage_attach_volumes_mock.assert_called_once_with(
|
||||
task.driver.storage, task)
|
||||
build_instance_info_mock.assert_called_once_with(task)
|
||||
build_options_mock.assert_called_once_with(task.node)
|
||||
pxe_prepare_ramdisk_mock.assert_called_once_with(
|
||||
task, {'a': 'b'})
|
||||
power_on_node_if_needed_mock.assert_called_once_with(task)
|
||||
restore_power_state_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
self.node.refresh()
|
||||
self.assertEqual('bar', self.node.instance_info['foo'])
|
||||
|
||||
@mock.patch.object(manager_utils, 'restore_power_state_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(noop_storage.NoopStorage, 'detach_volumes',
|
||||
autospec=True)
|
||||
@mock.patch.object(flat_network.FlatNetwork,
|
||||
'remove_provisioning_network',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(flat_network.FlatNetwork,
|
||||
'unconfigure_tenant_networks',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch('ironic.conductor.utils.node_power_action', autospec=True)
|
||||
def test_tear_down_with_smartnic_port(
|
||||
self, power_mock, unconfigure_tenant_nets_mock,
|
||||
remove_provisioning_net_mock, storage_detach_volumes_mock,
|
||||
power_on_node_if_needed_mock, restore_power_state_mock):
|
||||
object_utils.create_test_volume_target(
|
||||
self.context, node_id=self.node.id)
|
||||
node = self.node
|
||||
node.network_interface = 'flat'
|
||||
node.save()
|
||||
with task_manager.acquire(
|
||||
self.context, self.node['uuid'], shared=False) as task:
|
||||
power_on_node_if_needed_mock.return_value = states.POWER_OFF
|
||||
driver_return = self.driver.tear_down(task)
|
||||
power_mock.assert_called_once_with(task, states.POWER_OFF)
|
||||
self.assertEqual(driver_return, states.DELETED)
|
||||
unconfigure_tenant_nets_mock.assert_called_once_with(mock.ANY,
|
||||
task)
|
||||
remove_provisioning_net_mock.assert_called_once_with(mock.ANY,
|
||||
task)
|
||||
storage_detach_volumes_mock.assert_called_once_with(
|
||||
task.driver.storage, task)
|
||||
power_on_node_if_needed_mock.assert_called_once_with(task)
|
||||
restore_power_state_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
# Verify no volumes exist for new task instances.
|
||||
with task_manager.acquire(
|
||||
self.context, self.node['uuid'], shared=False) as task:
|
||||
self.assertEqual(0, len(task.volume_targets))
|
||||
|
||||
@mock.patch.object(manager_utils, 'restore_power_state_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_instance', autospec=True)
|
||||
@mock.patch.object(noop_storage.NoopStorage, 'should_write_image',
|
||||
autospec=True)
|
||||
def test_deploy_storage_should_write_image_false_with_smartnic_port(
|
||||
self, mock_write, mock_pxe_instance,
|
||||
power_on_node_if_needed_mock, restore_power_state_mock):
|
||||
mock_write.return_value = False
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.deploy_step = {
|
||||
'step': 'deploy', 'priority': 50, 'interface': 'deploy'}
|
||||
self.node.save()
|
||||
with task_manager.acquire(
|
||||
self.context, self.node['uuid'], shared=False) as task:
|
||||
power_on_node_if_needed_mock.return_value = states.POWER_OFF
|
||||
driver_return = self.driver.deploy(task)
|
||||
self.assertIsNone(driver_return)
|
||||
self.assertTrue(mock_pxe_instance.called)
|
||||
power_on_node_if_needed_mock.assert_called_once_with(task)
|
||||
restore_power_state_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
|
||||
|
||||
class AgentRAIDTestCase(db_base.DbTestCase):
|
||||
|
||||
|
@ -1807,3 +1942,74 @@ class AgentRescueTestCase(db_base.DbTestCase):
|
|||
self.assertNotIn('rescue_password', task.node.instance_info)
|
||||
self.assertFalse(mock_clean_ramdisk.called)
|
||||
mock_remove_rescue_net.assert_called_once_with(mock.ANY, task)
|
||||
|
||||
@mock.patch.object(manager_utils, 'restore_power_state_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(flat_network.FlatNetwork, 'add_rescuing_network',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(flat_network.FlatNetwork, 'unconfigure_tenant_networks',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(fake.FakeBoot, 'prepare_ramdisk', autospec=True)
|
||||
@mock.patch.object(fake.FakeBoot, 'clean_up_instance', autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'build_agent_options', autospec=True)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
def test_agent_rescue_with_smartnic_port(
|
||||
self, mock_node_power_action, mock_build_agent_opts,
|
||||
mock_clean_up_instance, mock_prepare_ramdisk,
|
||||
mock_unconf_tenant_net, mock_add_rescue_net,
|
||||
power_on_node_if_needed_mock, restore_power_state_mock):
|
||||
self.config(manage_agent_boot=True, group='agent')
|
||||
mock_build_agent_opts.return_value = {'ipa-api-url': 'fake-api'}
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
power_on_node_if_needed_mock.return_value = states.POWER_OFF
|
||||
result = task.driver.rescue.rescue(task)
|
||||
mock_node_power_action.assert_has_calls(
|
||||
[mock.call(task, states.POWER_OFF),
|
||||
mock.call(task, states.POWER_ON)])
|
||||
mock_clean_up_instance.assert_called_once_with(mock.ANY, task)
|
||||
mock_unconf_tenant_net.assert_called_once_with(mock.ANY, task)
|
||||
mock_add_rescue_net.assert_called_once_with(mock.ANY, task)
|
||||
mock_build_agent_opts.assert_called_once_with(task.node)
|
||||
mock_prepare_ramdisk.assert_called_once_with(
|
||||
mock.ANY, task, {'ipa-api-url': 'fake-api'})
|
||||
self.assertEqual(states.RESCUEWAIT, result)
|
||||
power_on_node_if_needed_mock.assert_called_once_with(task)
|
||||
restore_power_state_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
|
||||
@mock.patch.object(manager_utils, 'restore_power_state_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(flat_network.FlatNetwork, 'remove_rescuing_network',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(flat_network.FlatNetwork, 'configure_tenant_networks',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(fake.FakeBoot, 'prepare_instance', autospec=True)
|
||||
@mock.patch.object(fake.FakeBoot, 'clean_up_ramdisk', autospec=True)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
def test_agent_unrescue_with_smartnic_port(
|
||||
self, mock_node_power_action, mock_clean_ramdisk,
|
||||
mock_prepare_instance, mock_conf_tenant_net,
|
||||
mock_remove_rescue_net, power_on_node_if_needed_mock,
|
||||
restore_power_state_mock):
|
||||
self.config(manage_agent_boot=True, group='agent')
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
power_on_node_if_needed_mock.return_value = states.POWER_OFF
|
||||
result = task.driver.rescue.unrescue(task)
|
||||
mock_node_power_action.assert_has_calls(
|
||||
[mock.call(task, states.POWER_OFF),
|
||||
mock.call(task, states.POWER_ON)])
|
||||
mock_clean_ramdisk.assert_called_once_with(
|
||||
mock.ANY, task)
|
||||
mock_remove_rescue_net.assert_called_once_with(mock.ANY, task)
|
||||
mock_conf_tenant_net.assert_called_once_with(mock.ANY, task)
|
||||
mock_prepare_instance.assert_called_once_with(mock.ANY, task)
|
||||
self.assertEqual(states.ACTIVE, result)
|
||||
self.assertEqual(2, power_on_node_if_needed_mock.call_count)
|
||||
self.assertEqual(2, power_on_node_if_needed_mock.call_count)
|
||||
restore_power_state_mock.assert_has_calls(
|
||||
[mock.call(task, states.POWER_OFF),
|
||||
mock.call(task, states.POWER_OFF)])
|
||||
|
|
|
@ -424,8 +424,38 @@ class AgentRescueTests(AgentDeployMixinBaseTest):
|
|||
self.deploy._finalize_rescue, task)
|
||||
mock_finalize_rescue.assert_called_once_with(task.node)
|
||||
|
||||
@mock.patch.object(manager_utils, 'restore_power_state_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(agent.AgentRescue, 'clean_up',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(agent_client.AgentClient, 'finalize_rescue',
|
||||
spec=types.FunctionType)
|
||||
def test__finalize_rescue_with_smartnic_port(
|
||||
self, mock_finalize_rescue, mock_clean_up,
|
||||
power_on_node_if_needed_mock, restore_power_state_mock):
|
||||
node = self.node
|
||||
node.provision_state = states.RESCUEWAIT
|
||||
node.save()
|
||||
mock_finalize_rescue.return_value = {'command_status': 'SUCCEEDED'}
|
||||
with task_manager.acquire(self.context, self.node['uuid'],
|
||||
shared=False) as task:
|
||||
task.driver.network.configure_tenant_networks = mock.Mock()
|
||||
task.process_event = mock.Mock()
|
||||
power_on_node_if_needed_mock.return_value = states.POWER_OFF
|
||||
self.deploy._finalize_rescue(task)
|
||||
mock_finalize_rescue.assert_called_once_with(task.node)
|
||||
task.process_event.assert_has_calls([mock.call('resume'),
|
||||
mock.call('done')])
|
||||
mock_clean_up.assert_called_once_with(mock.ANY, task)
|
||||
power_on_node_if_needed_mock.assert_called_once_with(task)
|
||||
restore_power_state_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
|
||||
|
||||
class AgentDeployMixinTest(AgentDeployMixinBaseTest):
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed')
|
||||
@mock.patch.object(manager_utils, 'notify_conductor_resume_deploy',
|
||||
autospec=True)
|
||||
@mock.patch.object(driver_utils, 'collect_ramdisk_logs', autospec=True)
|
||||
|
@ -437,7 +467,8 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
|
|||
spec=types.FunctionType)
|
||||
def test_reboot_and_finish_deploy(
|
||||
self, power_off_mock, get_power_state_mock,
|
||||
node_power_action_mock, collect_mock, resume_mock):
|
||||
node_power_action_mock, collect_mock, resume_mock,
|
||||
power_on_node_if_needed_mock):
|
||||
cfg.CONF.set_override('deploy_logs_collect', 'always', 'agent')
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
|
@ -448,6 +479,8 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
|
|||
shared=True) as task:
|
||||
get_power_state_mock.side_effect = [states.POWER_ON,
|
||||
states.POWER_OFF]
|
||||
|
||||
power_on_node_if_needed_mock.return_value = None
|
||||
self.deploy.reboot_and_finish_deploy(task)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
self.assertEqual(2, get_power_state_mock.call_count)
|
||||
|
@ -458,6 +491,8 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
|
|||
collect_mock.assert_called_once_with(task.node)
|
||||
resume_mock.assert_called_once_with(task)
|
||||
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'notify_conductor_resume_deploy',
|
||||
autospec=True)
|
||||
@mock.patch.object(driver_utils, 'collect_ramdisk_logs', autospec=True)
|
||||
|
@ -469,7 +504,8 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
|
|||
spec=types.FunctionType)
|
||||
def test_reboot_and_finish_deploy_deprecated(
|
||||
self, power_off_mock, get_power_state_mock,
|
||||
node_power_action_mock, collect_mock, resume_mock):
|
||||
node_power_action_mock, collect_mock, resume_mock,
|
||||
power_on_node_if_needed_mock):
|
||||
# TODO(rloo): no deploy steps; delete this when we remove support
|
||||
# for handling no deploy steps.
|
||||
cfg.CONF.set_override('deploy_logs_collect', 'always', 'agent')
|
||||
|
@ -481,6 +517,7 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
|
|||
shared=True) as task:
|
||||
get_power_state_mock.side_effect = [states.POWER_ON,
|
||||
states.POWER_OFF]
|
||||
power_on_node_if_needed_mock.return_value = None
|
||||
self.deploy.reboot_and_finish_deploy(task)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
self.assertEqual(2, get_power_state_mock.call_count)
|
||||
|
@ -491,6 +528,8 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
|
|||
collect_mock.assert_called_once_with(task.node)
|
||||
self.assertFalse(resume_mock.called)
|
||||
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed',
|
||||
autospec=True)
|
||||
@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)
|
||||
|
@ -505,12 +544,14 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
|
|||
def test_reboot_and_finish_deploy_soft_poweroff_doesnt_complete(
|
||||
self, configure_tenant_net_mock, remove_provisioning_net_mock,
|
||||
power_off_mock, get_power_state_mock,
|
||||
node_power_action_mock, mock_collect):
|
||||
node_power_action_mock, mock_collect,
|
||||
power_on_node_if_needed_mock):
|
||||
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=True) as task:
|
||||
power_on_node_if_needed_mock.return_value = None
|
||||
get_power_state_mock.return_value = states.POWER_ON
|
||||
self.deploy.reboot_and_finish_deploy(task)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
|
@ -554,6 +595,7 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
|
|||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
self.assertFalse(mock_collect.called)
|
||||
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed')
|
||||
@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)
|
||||
|
@ -568,13 +610,14 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
|
|||
def test_reboot_and_finish_deploy_get_power_state_fails(
|
||||
self, configure_tenant_net_mock, remove_provisioning_net_mock,
|
||||
power_off_mock, get_power_state_mock, node_power_action_mock,
|
||||
mock_collect):
|
||||
mock_collect, power_on_node_if_needed_mock):
|
||||
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=True) as task:
|
||||
get_power_state_mock.side_effect = RuntimeError("boom")
|
||||
power_on_node_if_needed_mock.return_value = None
|
||||
self.deploy.reboot_and_finish_deploy(task)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
self.assertEqual(7, get_power_state_mock.call_count)
|
||||
|
@ -588,6 +631,8 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
|
|||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
self.assertFalse(mock_collect.called)
|
||||
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed',
|
||||
autospec=True)
|
||||
@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)
|
||||
|
@ -602,11 +647,12 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
|
|||
def test_reboot_and_finish_deploy_configure_tenant_network_exception(
|
||||
self, configure_tenant_net_mock, remove_provisioning_net_mock,
|
||||
power_off_mock, get_power_state_mock, node_power_action_mock,
|
||||
mock_collect):
|
||||
mock_collect, power_on_node_if_needed_mock):
|
||||
self.node.network_interface = 'neutron'
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.save()
|
||||
power_on_node_if_needed_mock.return_value = None
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
configure_tenant_net_mock.side_effect = exception.NetworkError(
|
||||
|
@ -649,6 +695,8 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
|
|||
self.assertEqual(states.ACTIVE, task.node.target_provision_state)
|
||||
mock_collect.assert_called_once_with(task.node)
|
||||
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed',
|
||||
autospec=True)
|
||||
@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)
|
||||
|
@ -663,12 +711,14 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
|
|||
def test_reboot_and_finish_deploy_power_on_fails(
|
||||
self, configure_tenant_net_mock, remove_provisioning_net_mock,
|
||||
power_off_mock, get_power_state_mock,
|
||||
node_power_action_mock, mock_collect):
|
||||
node_power_action_mock, mock_collect,
|
||||
power_on_node_if_needed_mock):
|
||||
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=True) as task:
|
||||
power_on_node_if_needed_mock.return_value = None
|
||||
get_power_state_mock.return_value = states.POWER_ON
|
||||
node_power_action_mock.side_effect = [None,
|
||||
RuntimeError("boom")]
|
||||
|
@ -1443,6 +1493,46 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
|
|||
hook_returned = agent_base_vendor._get_post_clean_step_hook(self.node)
|
||||
self.assertIsNone(hook_returned)
|
||||
|
||||
@mock.patch.object(manager_utils, 'restore_power_state_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed')
|
||||
@mock.patch.object(manager_utils, 'notify_conductor_resume_deploy',
|
||||
autospec=True)
|
||||
@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(fake.FakePower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
def test_reboot_and_finish_deploy_with_smartnic_port(
|
||||
self, power_off_mock, get_power_state_mock,
|
||||
node_power_action_mock, collect_mock, resume_mock,
|
||||
power_on_node_if_needed_mock, restore_power_state_mock):
|
||||
cfg.CONF.set_override('deploy_logs_collect', 'always', 'agent')
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.deploy_step = {
|
||||
'step': 'deploy', 'priority': 50, 'interface': 'deploy'}
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
get_power_state_mock.side_effect = [states.POWER_ON,
|
||||
states.POWER_OFF]
|
||||
power_on_node_if_needed_mock.return_value = states.POWER_OFF
|
||||
self.deploy.reboot_and_finish_deploy(task)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
self.assertEqual(2, get_power_state_mock.call_count)
|
||||
node_power_action_mock.assert_called_once_with(
|
||||
task, states.POWER_ON)
|
||||
self.assertEqual(states.DEPLOYING, task.node.provision_state)
|
||||
self.assertEqual(states.ACTIVE, task.node.target_provision_state)
|
||||
collect_mock.assert_called_once_with(task.node)
|
||||
resume_mock.assert_called_once_with(task)
|
||||
power_on_node_if_needed_mock.assert_called_once_with(task)
|
||||
restore_power_state_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
|
||||
|
||||
class TestRefreshCleanSteps(AgentDeployMixinBaseTest):
|
||||
|
||||
|
|
|
@ -943,6 +943,124 @@ class ISCSIDeployTestCase(db_base.DbTestCase):
|
|||
set_boot_device_mock.assert_called_once_with(
|
||||
mock.ANY, task, device=boot_devices.DISK, persistent=True)
|
||||
|
||||
@mock.patch.object(manager_utils, 'restore_power_state_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed', autospec=True)
|
||||
@mock.patch.object(noop_storage.NoopStorage, 'attach_volumes',
|
||||
autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'populate_storage_driver_internal_info',
|
||||
autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'build_agent_options', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_ramdisk', autospec=True)
|
||||
@mock.patch.object(flat_network.FlatNetwork, 'add_provisioning_network',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(flat_network.FlatNetwork,
|
||||
'unconfigure_tenant_networks',
|
||||
spec_set=True, autospec=True)
|
||||
def test_prepare_node_deploying_with_smartnic_port(
|
||||
self, unconfigure_tenant_net_mock, add_provisioning_net_mock,
|
||||
mock_prepare_ramdisk, mock_agent_options,
|
||||
storage_driver_info_mock, storage_attach_volumes_mock,
|
||||
power_on_node_if_needed_mock, restore_power_state_mock):
|
||||
mock_agent_options.return_value = {'c': 'd'}
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.provision_state = states.DEPLOYING
|
||||
power_on_node_if_needed_mock.return_value = states.POWER_OFF
|
||||
task.driver.deploy.prepare(task)
|
||||
mock_agent_options.assert_called_once_with(task.node)
|
||||
mock_prepare_ramdisk.assert_called_once_with(
|
||||
task.driver.boot, task, {'c': 'd'})
|
||||
add_provisioning_net_mock.assert_called_once_with(mock.ANY, task)
|
||||
unconfigure_tenant_net_mock.assert_called_once_with(mock.ANY, task)
|
||||
storage_driver_info_mock.assert_called_once_with(task)
|
||||
storage_attach_volumes_mock.assert_called_once_with(
|
||||
task.driver.storage, task)
|
||||
power_on_node_if_needed_mock.assert_called_once_with(task)
|
||||
restore_power_state_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
|
||||
@mock.patch.object(manager_utils, 'restore_power_state_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed', autospec=True)
|
||||
@mock.patch.object(noop_storage.NoopStorage, 'detach_volumes',
|
||||
autospec=True)
|
||||
@mock.patch.object(flat_network.FlatNetwork,
|
||||
'remove_provisioning_network',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(flat_network.FlatNetwork,
|
||||
'unconfigure_tenant_networks',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
def test_tear_down_with_smartnic_port(
|
||||
self, node_power_action_mock, unconfigure_tenant_nets_mock,
|
||||
remove_provisioning_net_mock, storage_detach_volumes_mock,
|
||||
power_on_node_if_needed_mock, restore_power_state_mock):
|
||||
obj_utils.create_test_volume_target(
|
||||
self.context, node_id=self.node.id)
|
||||
with task_manager.acquire(
|
||||
self.context, self.node.uuid, shared=False) as task:
|
||||
power_on_node_if_needed_mock.return_value = states.POWER_OFF
|
||||
state = task.driver.deploy.tear_down(task)
|
||||
self.assertEqual(state, states.DELETED)
|
||||
node_power_action_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
unconfigure_tenant_nets_mock.assert_called_once_with(
|
||||
mock.ANY, task)
|
||||
remove_provisioning_net_mock.assert_called_once_with(
|
||||
mock.ANY, task)
|
||||
storage_detach_volumes_mock.assert_called_once_with(
|
||||
task.driver.storage, task)
|
||||
power_on_node_if_needed_mock.assert_called_once_with(task)
|
||||
restore_power_state_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
# Verify no volumes exist for new task instances.
|
||||
with task_manager.acquire(self.context,
|
||||
self.node.uuid, shared=False) as task:
|
||||
self.assertEqual(0, len(task.volume_targets))
|
||||
|
||||
@mock.patch.object(manager_utils, 'restore_power_state_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed', autospec=True)
|
||||
@mock.patch.object(noop_storage.NoopStorage, 'should_write_image',
|
||||
autospec=True)
|
||||
@mock.patch.object(flat_network.FlatNetwork,
|
||||
'configure_tenant_networks',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(flat_network.FlatNetwork,
|
||||
'remove_provisioning_network',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot,
|
||||
'prepare_instance',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(iscsi_deploy, 'check_image_size', autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'cache_instance_image', autospec=True)
|
||||
def test_deploy_storage_check_write_image_false_with_smartnic_port(
|
||||
self, mock_cache_instance_image, mock_check_image_size,
|
||||
mock_node_power_action, mock_prepare_instance,
|
||||
mock_remove_network, mock_tenant_network, mock_write,
|
||||
power_on_node_if_needed_mock, restore_power_state_mock):
|
||||
mock_write.return_value = False
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.deploy_step = {
|
||||
'step': 'deploy', 'priority': 50, 'interface': 'deploy'}
|
||||
self.node.save()
|
||||
with task_manager.acquire(
|
||||
self.context, self.node.uuid, shared=False) as task:
|
||||
power_on_node_if_needed_mock.return_value = states.POWER_OFF
|
||||
ret = task.driver.deploy.deploy(task)
|
||||
self.assertIsNone(ret)
|
||||
self.assertFalse(mock_cache_instance_image.called)
|
||||
self.assertFalse(mock_check_image_size.called)
|
||||
mock_remove_network.assert_called_once_with(mock.ANY, task)
|
||||
mock_tenant_network.assert_called_once_with(mock.ANY, task)
|
||||
mock_prepare_instance.assert_called_once_with(mock.ANY, task)
|
||||
self.assertEqual(2, mock_node_power_action.call_count)
|
||||
self.assertEqual(states.DEPLOYING, task.node.provision_state)
|
||||
power_on_node_if_needed_mock.assert_called_once_with(task)
|
||||
restore_power_state_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
|
||||
|
||||
# Cleanup of iscsi_deploy with pxe boot interface
|
||||
class CleanUpFullFlowTestCase(db_base.DbTestCase):
|
||||
|
|
|
@ -1070,6 +1070,44 @@ class PXERamdiskDeployTestCase(db_base.DbTestCase):
|
|||
task.driver.deploy.validate(task)
|
||||
mock_validate.assert_called_once_with(mock.ANY, task)
|
||||
|
||||
@mock.patch.object(manager_utils, 'restore_power_state_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'power_on_node_if_needed',
|
||||
autospec=True)
|
||||
@mock.patch.object(pxe.LOG, 'warning', autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'switch_pxe_config', autospec=True)
|
||||
@mock.patch.object(dhcp_factory, 'DHCPFactory', autospec=True)
|
||||
@mock.patch.object(pxe_utils, 'cache_ramdisk_kernel', autospec=True)
|
||||
@mock.patch.object(pxe_utils, 'get_instance_image_info', autospec=True)
|
||||
def test_deploy_with_smartnic_port(
|
||||
self, mock_image_info, mock_cache,
|
||||
mock_dhcp_factory, mock_switch_config, mock_warning,
|
||||
power_on_node_if_needed_mock, restore_power_state_mock):
|
||||
image_info = {'kernel': ('', '/path/to/kernel'),
|
||||
'ramdisk': ('', '/path/to/ramdisk')}
|
||||
mock_image_info.return_value = image_info
|
||||
i_info = self.node.instance_info
|
||||
i_info.update({'capabilities': {'boot_option': 'ramdisk'}})
|
||||
self.node.instance_info = i_info
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
power_on_node_if_needed_mock.return_value = states.POWER_OFF
|
||||
self.assertIsNone(task.driver.deploy.deploy(task))
|
||||
mock_image_info.assert_called_once_with(task)
|
||||
mock_cache.assert_called_once_with(
|
||||
task, image_info, ipxe_enabled=CONF.pxe.ipxe_enabled)
|
||||
self.assertFalse(mock_warning.called)
|
||||
power_on_node_if_needed_mock.assert_called_once_with(task)
|
||||
restore_power_state_mock.assert_called_once_with(
|
||||
task, states.POWER_OFF)
|
||||
i_info['configdrive'] = 'meow'
|
||||
self.node.instance_info = i_info
|
||||
self.node.save()
|
||||
mock_warning.reset_mock()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertIsNone(task.driver.deploy.deploy(task))
|
||||
self.assertTrue(mock_warning.called)
|
||||
|
||||
|
||||
class PXEValidateRescueTestCase(db_base.DbTestCase):
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
prelude: >
|
||||
Add support for Smart NICs in baremetal servers.
|
||||
features:
|
||||
- |
|
||||
Enable use of Smart NICs by extending ironic to implement generic
|
||||
networking services for baremetal servers.
|
||||
|
||||
Extending the ramdisk, direct, iscsi and ansible deployment Interfaces
|
||||
to support the Smart NIC use-cases.
|
Loading…
Reference in New Issue