Allow to set Neutron port setup delay from config

Unfortunately there still no mechanism to synchronize events with Neutron.
Sometimes server can get to PXE faster than Neutron configures port for netboot.
It may occur with VMs, and hardware servers with fast boot enabled.
This patch introduce new config variable 'port_setup_delay'
that allows to wait for Neutron operations.

Change-Id: Iaeb78649a92b89e2c6eb0f545aed95a766d14430
This commit is contained in:
vsaienko 2016-03-16 11:31:38 +02:00
parent 3d80e41e8d
commit 5cf9f083fe
5 changed files with 119 additions and 7 deletions

View File

@ -506,6 +506,10 @@ function configure_ironic_conductor {
iniset $IRONIC_CONF_FILE deploy http_root $IRONIC_HTTP_DIR
iniset $IRONIC_CONF_FILE deploy http_url "http://$IRONIC_HTTP_SERVER:$IRONIC_HTTP_PORT"
fi
if [[ "$IRONIC_IS_HARDWARE" == "False" ]]; then
iniset $IRONIC_CONF_FILE neutron port_setup_delay 15
fi
}
# create_ironic_cache_dir() - Part of the init_ironic() process

View File

@ -1559,6 +1559,11 @@
# value)
#url_timeout = 30
# Delay value to wait for Neutron agents to setup sufficient
# DHCP configuration for port. (integer value)
# Minimum value: 0
#port_setup_delay = 0
# Client retries in the case of a failed request. (integer
# value)
#retries = 3

View File

@ -40,6 +40,11 @@ neutron_opts = [
cfg.IntOpt('url_timeout',
default=30,
help=_('Timeout value for connecting to neutron in seconds.')),
cfg.IntOpt('port_setup_delay',
default=0,
min=0,
help=_('Delay value to wait for Neutron agents to setup '
'sufficient DHCP configuration for port.')),
cfg.IntOpt('retries',
default=3,
help=_('Client retries in the case of a failed request.')),
@ -193,13 +198,22 @@ class NeutronDHCPApi(base.BaseDHCP):
{'node': task.node.uuid, 'ports': failures})
# TODO(adam_g): Hack to workaround bug 1334447 until we have a
# mechanism for synchronizing events with Neutron. We need to sleep
# only if we are booting VMs, which is implied by SSHPower, to ensure
# they do not boot before Neutron agents have setup sufficient DHCP
# config for netboot.
if isinstance(task.driver.power, ssh.SSHPower):
LOG.debug("Waiting 15 seconds for Neutron.")
time.sleep(15)
# mechanism for synchronizing events with Neutron. We need to sleep
# only if server gets to PXE faster than Neutron agents have setup
# sufficient DHCP config for netboot. It may occur when we are using
# VMs or hardware server with fast boot enabled.
port_delay = CONF.neutron.port_setup_delay
# TODO(vsaienko) remove hardcoded value for SSHPower driver
# after Newton release.
if isinstance(task.driver.power, ssh.SSHPower) and port_delay == 0:
LOG.warning(_LW("Setting the port delay to 15 for SSH power "
"driver by default, this will be removed in "
"Ocata release. Please set configuration "
"parameter port_setup_delay to 15."))
port_delay = 15
if port_delay != 0:
LOG.debug("Waiting %d seconds for Neutron.", port_delay)
time.sleep(port_delay)
def _get_fixed_ip_address(self, port_uuid, client):
"""Get a Neutron port's fixed ip address.

View File

@ -26,6 +26,7 @@ from ironic.common import exception
from ironic.common import pxe_utils
from ironic.conductor import task_manager
from ironic.dhcp import neutron
from ironic.drivers.modules import ssh
from ironic.tests.unit.conductor import mgr_utils
from ironic.tests.unit.db import base as db_base
from ironic.tests.unit.objects import utils as object_utils
@ -250,6 +251,87 @@ class TestNeutron(db_base.DbTestCase):
mock_gnvi.assert_called_once_with(task)
self.assertEqual(2, mock_updo.call_count)
@mock.patch('time.sleep', autospec=True)
@mock.patch.object(neutron.NeutronDHCPApi, 'update_port_dhcp_opts',
autospec=True)
@mock.patch('ironic.common.network.get_node_vif_ids', autospec=True)
def test_update_dhcp_set_sleep_and_ssh(self, mock_gnvi, mock_updo,
mock_ts):
mock_gnvi.return_value = {'ports': {'port-uuid': 'vif-uuid'},
'portgroups': {}}
self.config(port_setup_delay=30, group='neutron')
with task_manager.acquire(self.context,
self.node.uuid) as task:
task.driver.power = ssh.SSHPower()
opts = pxe_utils.dhcp_options_for_instance(task)
api = dhcp_factory.DHCPFactory()
api.update_dhcp(task, opts)
mock_ts.assert_called_with(30)
mock_updo.assert_called_once_with(mock.ANY, 'vif-uuid', opts,
token=self.context.auth_token)
@mock.patch.object(neutron, 'LOG', autospec=True)
@mock.patch('time.sleep', autospec=True)
@mock.patch.object(neutron.NeutronDHCPApi, 'update_port_dhcp_opts',
autospec=True)
@mock.patch('ironic.common.network.get_node_vif_ids', autospec=True)
def test_update_dhcp_unset_sleep_and_ssh(self, mock_gnvi, mock_updo,
mock_ts, mock_log):
mock_gnvi.return_value = {'ports': {'port-uuid': 'vif-uuid'},
'portgroups': {}}
with task_manager.acquire(self.context,
self.node.uuid) as task:
opts = pxe_utils.dhcp_options_for_instance(task)
task.driver.power = ssh.SSHPower()
api = dhcp_factory.DHCPFactory()
api.update_dhcp(task, opts)
self.assertTrue(mock_log.warning.called)
self.assertIn('Setting the port delay to 15 for SSH',
mock_log.warning.call_args[0][0])
mock_ts.assert_called_with(15)
mock_updo.assert_called_once_with(mock.ANY, 'vif-uuid', opts,
token=self.context.auth_token)
@mock.patch.object(neutron, 'LOG', autospec=True)
@mock.patch('time.sleep', autospec=True)
@mock.patch.object(neutron.NeutronDHCPApi, 'update_port_dhcp_opts',
autospec=True)
@mock.patch('ironic.common.network.get_node_vif_ids', autospec=True)
def test_update_dhcp_set_sleep_and_fake(self, mock_gnvi, mock_updo,
mock_ts, mock_log):
mock_gnvi.return_value = {'ports': {'port-uuid': 'vif-uuid'},
'portgroups': {}}
self.config(port_setup_delay=30, group='neutron')
with task_manager.acquire(self.context,
self.node.uuid) as task:
opts = pxe_utils.dhcp_options_for_instance(task)
api = dhcp_factory.DHCPFactory()
api.update_dhcp(task, opts)
mock_log.debug.assert_called_once_with(
"Waiting %d seconds for Neutron.", 30)
mock_log.warning.assert_not_called()
mock_ts.assert_called_with(30)
mock_updo.assert_called_once_with(mock.ANY, 'vif-uuid', opts,
token=self.context.auth_token)
@mock.patch.object(neutron, 'LOG', autospec=True)
@mock.patch.object(neutron.NeutronDHCPApi, 'update_port_dhcp_opts',
autospec=True)
@mock.patch('ironic.common.network.get_node_vif_ids', autospec=True)
def test_update_dhcp_unset_sleep_and_fake(self, mock_gnvi, mock_updo,
mock_log):
mock_gnvi.return_value = {'ports': {'port-uuid': 'vif-uuid'},
'portgroups': {}}
with task_manager.acquire(self.context,
self.node.uuid) as task:
opts = pxe_utils.dhcp_options_for_instance(task)
api = dhcp_factory.DHCPFactory()
api.update_dhcp(task, opts)
mock_log.debug.assert_not_called()
mock_log.warning.assert_not_called()
mock_updo.assert_called_once_with(mock.ANY, 'vif-uuid', opts,
token=self.context.auth_token)
def test__get_fixed_ip_address(self):
port_id = 'fake-port-id'
expected = "192.168.1.3"

View File

@ -0,0 +1,7 @@
---
other:
- Add Neutron ``port_setup_delay`` configuration
option. This delay allows Ironic to wait for
Neutron port operations until we have a
mechanism for synchronizing events with Neutron.
Set to 0 by default.