summaryrefslogtreecommitdiff
path: root/ironic_python_agent
diff options
context:
space:
mode:
authorJosh Gachnang <josh@pcsforeducation.com>2014-12-15 17:29:14 -0800
committerJosh Gachnang <josh@pcsforeducation.com>2014-12-16 17:59:29 -0800
commit417bf086a53da995fbf93cc2c04990f9c5f87c9f (patch)
treeeea61aed87cd0f021b05eeae526098abae9c0250 /ironic_python_agent
parent86d4b41709548cb304ab9cc0b627b39449eef121 (diff)
downloadironic-python-agent-417bf086a53da995fbf93cc2c04990f9c5f87c9f.tar.gz
Add standalone mode for IPA
This allows a developer to run IPA without an Ironic API. This can be useful for testing (especially functional testing) or testing integration of things like hardware managers. Change-Id: I2dc49fbe306430bf5b05a36fe56de5275fc128b2
Diffstat (limited to 'ironic_python_agent')
-rw-r--r--ironic_python_agent/agent.py21
-rw-r--r--ironic_python_agent/cmd/agent.py14
-rw-r--r--ironic_python_agent/tests/agent.py50
3 files changed, 70 insertions, 15 deletions
diff --git a/ironic_python_agent/agent.py b/ironic_python_agent/agent.py
index 5bf2eeca..ab4593b8 100644
--- a/ironic_python_agent/agent.py
+++ b/ironic_python_agent/agent.py
@@ -137,7 +137,7 @@ class IronicPythonAgent(base.ExecuteCommandMixin):
def __init__(self, api_url, advertise_address, listen_address,
ip_lookup_attempts, ip_lookup_sleep, network_interface,
- lookup_timeout, lookup_interval, driver_name):
+ lookup_timeout, lookup_interval, driver_name, standalone):
super(IronicPythonAgent, self).__init__()
self.ext_mgr = extension.ExtensionManager(
namespace='ironic_python_agent.extensions',
@@ -166,6 +166,7 @@ class IronicPythonAgent(base.ExecuteCommandMixin):
self.ip_lookup_attempts = ip_lookup_attempts
self.ip_lookup_sleep = ip_lookup_sleep
self.network_interface = network_interface
+ self.standalone = standalone
def get_status(self):
"""Retrieve a serializable status.
@@ -267,20 +268,22 @@ class IronicPythonAgent(base.ExecuteCommandMixin):
result_id)
def force_heartbeat(self):
- self.heartbeater.force_heartbeat()
+ if not self.standalone:
+ self.heartbeater.force_heartbeat()
def run(self):
"""Run the Ironic Python Agent."""
# Get the UUID so we can heartbeat to Ironic. Raises LookupNodeError
# if there is an issue (uncaught, restart agent)
self.started_at = _time()
- content = self.api_client.lookup_node(
+ if not self.standalone:
+ content = self.api_client.lookup_node(
hardware_info=self.hardware.list_hardware_info(),
timeout=self.lookup_timeout,
starting_interval=self.lookup_interval)
- self.node = content['node']
- self.heartbeat_timeout = content['heartbeat_timeout']
+ self.node = content['node']
+ self.heartbeat_timeout = content['heartbeat_timeout']
wsgi = simple_server.make_server(
self.listen_address[0],
@@ -288,12 +291,14 @@ class IronicPythonAgent(base.ExecuteCommandMixin):
self.api,
server_class=simple_server.WSGIServer)
- # Don't start heartbeating until the server is listening
- self.heartbeater.start()
+ if not self.standalone:
+ # Don't start heartbeating until the server is listening
+ self.heartbeater.start()
try:
wsgi.serve_forever()
except BaseException:
self.log.exception('shutting down')
- self.heartbeater.stop()
+ if not self.standalone:
+ self.heartbeater.stop()
diff --git a/ironic_python_agent/cmd/agent.py b/ironic_python_agent/cmd/agent.py
index 6b26ed37..e9f38105 100644
--- a/ironic_python_agent/cmd/agent.py
+++ b/ironic_python_agent/cmd/agent.py
@@ -128,8 +128,7 @@ APARAMS = _get_agent_params()
cli_opts = [
cfg.StrOpt('api_url',
- required=('ipa-api-url' not in APARAMS),
- default=APARAMS.get('ipa-api-url'),
+ default=APARAMS.get('ipa-api-url', 'http://127.0.0.1:6835'),
deprecated_name='api-url',
help='URL of the Ironic API'),
@@ -195,7 +194,12 @@ cli_opts = [
cfg.FloatOpt('lldp_timeout',
default=APARAMS.get('lldp-timeout', 30.0),
- help='The amount of seconds to wait for LLDP packets.')
+ help='The amount of seconds to wait for LLDP packets.'),
+
+ cfg.BoolOpt('standalone',
+ default=False,
+ help='Note: for debugging only. Start the Agent but suppress '
+ 'any calls to Ironic API.'),
]
CONF.register_cli_opts(cli_opts)
@@ -204,7 +208,6 @@ CONF.register_cli_opts(cli_opts)
def run():
CONF()
log.setup('ironic-python-agent')
-
agent.IronicPythonAgent(CONF.api_url,
(CONF.advertise_host, CONF.advertise_port),
(CONF.listen_host, CONF.listen_port),
@@ -213,4 +216,5 @@ def run():
CONF.network_interface,
CONF.lookup_timeout,
CONF.lookup_interval,
- CONF.driver_name).run()
+ CONF.driver_name,
+ CONF.standalone).run()
diff --git a/ironic_python_agent/tests/agent.py b/ironic_python_agent/tests/agent.py
index 32c79f85..705172bc 100644
--- a/ironic_python_agent/tests/agent.py
+++ b/ironic_python_agent/tests/agent.py
@@ -146,7 +146,8 @@ class TestBaseAgent(test_base.BaseTestCase):
'eth0',
300,
1,
- 'agent_ipmitool')
+ 'agent_ipmitool',
+ False)
self.agent.ext_mgr = extension.ExtensionManager.\
make_test_instance([extension.Extension('fake', None,
FakeExtension,
@@ -210,7 +211,8 @@ class TestBaseAgent(test_base.BaseTestCase):
None,
300,
1,
- 'agent_ipmitool')
+ 'agent_ipmitool',
+ False)
homeless_agent.hardware = mock.Mock()
mock_list_net = homeless_agent.hardware.list_network_interfaces
@@ -303,6 +305,50 @@ class TestBaseAgent(test_base.BaseTestCase):
self.agent.get_node_uuid)
+class TestAgentStandalone(test_base.BaseTestCase):
+
+ def setUp(self):
+ super(TestAgentStandalone, self).setUp()
+ self.agent = agent.IronicPythonAgent('https://fake_api.example.'
+ 'org:8081/',
+ ('203.0.113.1', 9990),
+ ('192.0.2.1', 9999),
+ 3,
+ 10,
+ 'eth0',
+ 300,
+ 1,
+ 'agent_ipmitool',
+ True)
+
+ @mock.patch('wsgiref.simple_server.make_server', autospec=True)
+ @mock.patch.object(hardware.HardwareManager, 'list_hardware_info')
+ def test_run(self, mocked_list_hardware, wsgi_server_cls):
+ wsgi_server = wsgi_server_cls.return_value
+ wsgi_server.start.side_effect = KeyboardInterrupt()
+
+ self.agent.heartbeater = mock.Mock()
+ self.agent.api_client.lookup_node = mock.Mock()
+ self.agent.api_client.lookup_node.return_value = {
+ 'node': {
+ 'uuid': 'deadbeef-dabb-ad00-b105-f00d00bab10c'
+ },
+ 'heartbeat_timeout': 300
+ }
+ self.agent.run()
+
+ listen_addr = ('192.0.2.1', 9999)
+ wsgi_server_cls.assert_called_once_with(
+ listen_addr[0],
+ listen_addr[1],
+ self.agent.api,
+ server_class=simple_server.WSGIServer)
+ wsgi_server.serve_forever.assert_called_once()
+
+ self.assertFalse(self.agent.heartbeater.called)
+ self.assertFalse(self.agent.api_client.lookup_node.called)
+
+
class TestAgentCmd(test_base.BaseTestCase):
@mock.patch('ironic_python_agent.openstack.common.log.getLogger')
@mock.patch(OPEN_FUNCTION_NAME)