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:
parent
87a363979c
commit
a834065970
|
@ -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()
|
||||
|
|
|
@ -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'),
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue