Skip API related work if no api url configured

Currently, if IPA is booted without an ironic api url, it will default
to localhost and fail to connect. Instead, we now explicitly fail and
print a log message if no api callback url is provided.

Conflicts:
	ironic_python_agent/agent.py

Change-Id: I0271be94ba7febc6abd5bf3343f6fa179bc1a6a4
Closes-Bug: #1643966
(cherry picked from commit dd9253f1b6)
This commit is contained in:
Yufei 2016-11-19 20:30:35 +08:00 committed by Dmitry Tantsur
parent 87a363979c
commit a834065970
4 changed files with 141 additions and 27 deletions

View File

@ -170,14 +170,15 @@ class IronicPythonAgent(base.ExecuteCommandMixin):
)
self.api_url = api_url
self.driver_name = driver_name
self.api_client = ironic_api_client.APIClient(self.api_url,
self.driver_name)
if self.api_url:
self.api_client = ironic_api_client.APIClient(self.api_url,
self.driver_name)
self.heartbeater = IronicPythonAgentHeartbeater(self)
self.listen_address = listen_address
self.advertise_address = advertise_address
self.version = pkg_resources.get_distribution('ironic-python-agent')\
.version
self.api = app.VersionSelectorApplication(self)
self.heartbeater = IronicPythonAgentHeartbeater(self)
self.heartbeat_timeout = None
self.started_at = None
self.node = None
@ -331,30 +332,40 @@ class IronicPythonAgent(base.ExecuteCommandMixin):
if not self.standalone:
# Inspection should be started before call to lookup, otherwise
# lookup will fail due to unknown MAC.
uuid = inspector.inspect()
uuid = None
if cfg.CONF.inspection_callback_url:
uuid = inspector.inspect()
self._wait_for_interface()
content = self.api_client.lookup_node(
hardware_info=hardware.dispatch_to_managers(
'list_hardware_info'),
timeout=self.lookup_timeout,
starting_interval=self.lookup_interval,
node_uuid=uuid)
if self.api_url:
self._wait_for_interface()
content = self.api_client.lookup_node(
hardware_info=hardware.dispatch_to_managers(
'list_hardware_info'),
timeout=self.lookup_timeout,
starting_interval=self.lookup_interval,
node_uuid=uuid)
LOG.debug('Received lookup results: %s', content)
self.node = content['node']
LOG.info('Lookup succeeded, node UUID is %s', self.node['uuid'])
hardware.cache_node(self.node)
self.heartbeat_timeout = content['config']['heartbeat_timeout']
LOG.debug('Received lookup results: %s', content)
self.node = content['node']
LOG.info('Lookup succeeded, node UUID is %s',
self.node['uuid'])
hardware.cache_node(self.node)
self.heartbeat_timeout = content['config']['heartbeat_timeout']
# Update config with values from Ironic
config = content.get('config', {})
if config.get('metrics'):
for opt, val in config.items():
setattr(cfg.CONF.metrics, opt, val)
if config.get('metrics_statsd'):
for opt, val in config.items():
setattr(cfg.CONF.metrics_statsd, opt, val)
# Update config with values from Ironic
config = content.get('config', {})
if config.get('metrics'):
for opt, val in config.items():
setattr(cfg.CONF.metrics, opt, val)
if config.get('metrics_statsd'):
for opt, val in config.items():
setattr(cfg.CONF.metrics_statsd, opt, val)
elif cfg.CONF.inspection_callback_url:
LOG.info('No ipa-api-url configured, Heartbeat and lookup '
'skipped for inspector.')
else:
LOG.error('Neither ipa-api-url nor inspection_callback_url'
'found, please check your pxe append parameters.')
wsgi = simple_server.make_server(
self.listen_address.hostname,
@ -362,7 +373,7 @@ class IronicPythonAgent(base.ExecuteCommandMixin):
self.api,
server_class=simple_server.WSGIServer)
if not self.standalone:
if not self.standalone and self.api_url:
# Don't start heartbeating until the server is listening
self.heartbeater.start()
@ -371,5 +382,5 @@ class IronicPythonAgent(base.ExecuteCommandMixin):
except BaseException:
LOG.exception('shutting down')
if not self.standalone:
if not self.standalone and self.api_url:
self.heartbeater.stop()

View File

@ -23,7 +23,7 @@ APARAMS = utils.get_agent_params()
cli_opts = [
cfg.StrOpt('api_url',
default=APARAMS.get('ipa-api-url', 'http://127.0.0.1:6385'),
default=APARAMS.get('ipa-api-url'),
deprecated_name='api-url',
help='URL of the Ironic API'),

View File

@ -249,6 +249,104 @@ class TestBaseAgent(test_base.BaseTestCase):
mocked_dispatch.assert_called_once_with("list_hardware_info")
self.agent.heartbeater.start.assert_called_once_with()
@mock.patch.object(agent.IronicPythonAgent,
'_wait_for_interface')
@mock.patch.object(inspector, 'inspect', autospec=True)
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch('wsgiref.simple_server.make_server', autospec=True)
@mock.patch.object(hardware.HardwareManager, 'list_hardware_info')
def test_run_with_inspection_without_apiurl(self,
mocked_list_hardware,
mocked_server_maker,
mocked_dispatch,
mocked_inspector,
mocked_wait):
# If inspection_callback_url is configured and api_url is not when the
# agent starts, ensure that the inspection will be called and wsgi
# server will work as usual. Also, make sure api_client and heartbeater
# will not be initialized in this case.
CONF.set_override('inspection_callback_url', 'http://foo/bar',
enforce_type=True)
self.agent = agent.IronicPythonAgent(None,
agent.Host('203.0.113.1', 9990),
agent.Host('192.0.2.1', 9999),
3,
10,
'eth0',
300,
1,
'agent_ssh',
False)
self.assertFalse(hasattr(self.agent, 'api_client'))
self.assertFalse(hasattr(self.agent, 'heartbeater'))
wsgi_server = mocked_server_maker.return_value
wsgi_server.start.side_effect = KeyboardInterrupt()
self.agent.run()
listen_addr = agent.Host('192.0.2.1', 9999)
mocked_server_maker.assert_called_once_with(
listen_addr.hostname,
listen_addr.port,
self.agent.api,
server_class=simple_server.WSGIServer)
wsgi_server.serve_forever.assert_called_once_with()
mocked_inspector.assert_called_once_with()
self.assertFalse(mocked_wait.called)
self.assertFalse(mocked_dispatch.called)
@mock.patch.object(agent.IronicPythonAgent,
'_wait_for_interface')
@mock.patch.object(inspector, 'inspect', autospec=True)
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch('wsgiref.simple_server.make_server', autospec=True)
@mock.patch.object(hardware.HardwareManager, 'list_hardware_info')
def test_run_without_inspection_and_apiurl(self,
mocked_list_hardware,
mocked_server_maker,
mocked_dispatch,
mocked_inspector,
mocked_wait):
# If both api_url and inspection_callback_url are not configured when
# the agent starts, ensure that the inspection will be skipped and wsgi
# server will work as usual. Also, make sure api_client and heartbeater
# will not be initialized in this case.
CONF.set_override('inspection_callback_url', None,
enforce_type=True)
self.agent = agent.IronicPythonAgent(None,
agent.Host('203.0.113.1', 9990),
agent.Host('192.0.2.1', 9999),
3,
10,
'eth0',
300,
1,
'agent_ssh',
False)
self.assertFalse(hasattr(self.agent, 'api_client'))
self.assertFalse(hasattr(self.agent, 'heartbeater'))
wsgi_server = mocked_server_maker.return_value
wsgi_server.start.side_effect = KeyboardInterrupt()
self.agent.run()
listen_addr = agent.Host('192.0.2.1', 9999)
mocked_server_maker.assert_called_once_with(
listen_addr.hostname,
listen_addr.port,
self.agent.api,
server_class=simple_server.WSGIServer)
wsgi_server.serve_forever.assert_called_once_with()
self.assertFalse(mocked_inspector.called)
self.assertFalse(mocked_wait.called)
self.assertFalse(mocked_dispatch.called)
@mock.patch.object(time, 'time', autospec=True)
@mock.patch.object(time, 'sleep', autospec=True)
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)

View File

@ -0,0 +1,5 @@
---
fixes:
- If ipa-api-url is not configured in pxe parameters, skip lookup and heartbeat
to avoid ConnectionError when the IPA starts. See https://bugs.launchpad.net/bugs/1643966
for details.