From d0323f3ec1668f35a3153628d733678eb9dcf5d0 Mon Sep 17 00:00:00 2001 From: Josh Gachnang <josh@pcsforeducation.com> Date: Wed, 19 Mar 2014 13:55:56 -0700 Subject: [PATCH] Using advertise_address, using valid uuid/macs --- teeth_agent/agent.py | 21 +++++++++++--- teeth_agent/cmd/agent.py | 22 ++++++++++++--- teeth_agent/overlord_agent_api.py | 11 +++++--- teeth_agent/tests/agent.py | 5 ++-- teeth_agent/tests/overlord_agent_api.py | 37 +++++++++++++------------ 5 files changed, 64 insertions(+), 32 deletions(-) diff --git a/teeth_agent/agent.py b/teeth_agent/agent.py index b28bd256b..d6bffa85c 100644 --- a/teeth_agent/agent.py +++ b/teeth_agent/agent.py @@ -90,7 +90,10 @@ class TeethAgentHeartbeater(threading.Thread): def do_heartbeat(self): try: - deadline = self.api.heartbeat(uuid=self.agent.get_node_uuid()) + deadline = self.api.heartbeat( + uuid=self.agent.get_node_uuid(), + advertise_address=self.agent.advertise_address + ) self.error_delay = self.initial_delay self.log.info('heartbeat successful') except Exception: @@ -109,10 +112,11 @@ class TeethAgentHeartbeater(threading.Thread): class TeethAgent(object): - def __init__(self, api_url, listen_address): + def __init__(self, api_url, advertise_address, listen_address): self.api_url = api_url self.api_client = overlord_agent_api.APIClient(self.api_url) self.listen_address = listen_address + self.advertise_address = advertise_address self.mode_implementation = None self.version = pkg_resources.get_distribution('teeth-agent').version self.api = app.VersionSelectorApplication(self) @@ -142,6 +146,8 @@ class TeethAgent(object): return self.hardware.get_primary_mac_address() def get_node_uuid(self): + if 'uuid' not in self.node: + errors.HeartbeatError('Tried to heartbeat without node UUID.') return self.node['uuid'] def list_command_results(self): @@ -234,5 +240,12 @@ def _load_mode_implementation(mode_name): return mgr.driver -def build_agent(api_url, listen_host, listen_port): - return TeethAgent(api_url, (listen_host, listen_port)) +def build_agent(api_url, + advertise_host, + advertise_port, + listen_host, + listen_port): + + return TeethAgent(api_url=api_url, + advertise_address=(advertise_host, advertise_port), + listen_address=(listen_host, listen_port)) diff --git a/teeth_agent/cmd/agent.py b/teeth_agent/cmd/agent.py index 83124f6d3..f6e1a6148 100644 --- a/teeth_agent/cmd/agent.py +++ b/teeth_agent/cmd/agent.py @@ -37,8 +37,22 @@ def run(): default=9999, type=int, help='The port to listen on') - + parser.add_argument('--advertise-host', + default='0.0.0.0', + type=str, + help=('The port to tell Ironic to reply and send ' + 'commands to. This is different than ' + 'listen-host because Docker will have a ' + 'different internal IP than the host IP that ' + 'Ironic will be communicating with.')) + parser.add_argument('--advertise-port', + default=9999, + type=int, + help=('The port to tell Ironic to reply and send ' + 'commands to.')) args = parser.parse_args() - agent.build_agent(args.api_url, - args.listen_host, - args.listen_port).run() + agent.build_agent(api_url=args.api_url, + advertise_host=args.advertise_host, + advertise_port=args.advertise_port, + listen_host=args.listen_host, + listen_port=args.listen_port).run() diff --git a/teeth_agent/overlord_agent_api.py b/teeth_agent/overlord_agent_api.py index b233876a4..fda86b22b 100644 --- a/teeth_agent/overlord_agent_api.py +++ b/teeth_agent/overlord_agent_api.py @@ -46,13 +46,13 @@ class APIClient(object): headers=request_headers, data=data) - def heartbeat(self, uuid, listen_address): + def heartbeat(self, uuid, advertise_address): path = '/{api_version}/nodes/{uuid}/vendor_passthru/heartbeat'.format( api_version=self.api_version, uuid=uuid ) data = { - 'agent_url': self._get_agent_url(listen_address) + 'agent_url': self._get_agent_url(advertise_address) } try: response = self._request('POST', path, data=data) @@ -74,6 +74,8 @@ class APIClient(object): path = '/{api_version}/drivers/teeth/lookup'.format( api_version=self.api_version ) + # This hardware won't be saved on the node currently, because of how + # driver_vendor_passthru is implemented (no node saving). data = { 'hardware': hardware_info, } @@ -98,5 +100,6 @@ class APIClient(object): '{0}'.format(content)) return content['node'] - def _get_agent_url(self, listen_address): - return "http://{0}:{1}".format(listen_address[0], listen_address[1]) + def _get_agent_url(self, advertise_address): + return 'http://{0}:{1}'.format(advertise_address[0], + advertise_address[1]) diff --git a/teeth_agent/tests/agent.py b/teeth_agent/tests/agent.py index 4ea141fa5..a032a021d 100644 --- a/teeth_agent/tests/agent.py +++ b/teeth_agent/tests/agent.py @@ -120,7 +120,8 @@ class TestBaseAgent(unittest.TestCase): def setUp(self): self.encoder = encoding.RESTJSONEncoder(indent=4) self.agent = agent.TeethAgent('https://fake_api.example.org:8081/', - ('localhost', 9999)) + ('31.41.59.26', 9990), + ('42.42.42.42', 9999)) def assertEqualEncoded(self, a, b): # Evidently JSONEncoder.default() can't handle None (??) so we have to @@ -164,7 +165,7 @@ class TestBaseAgent(unittest.TestCase): self.agent.api_client.lookup_node = mock.Mock() self.agent.run() - listen_addr = ('localhost', 9999) + listen_addr = ('42.42.42.42', 9999) wsgi_server_cls.assert_called_once_with( listen_addr[0], listen_addr[1], diff --git a/teeth_agent/tests/overlord_agent_api.py b/teeth_agent/tests/overlord_agent_api.py index 7187270d3..32a8e0acb 100644 --- a/teeth_agent/tests/overlord_agent_api.py +++ b/teeth_agent/tests/overlord_agent_api.py @@ -32,9 +32,9 @@ class TestBaseTeethAgent(unittest.TestCase): self.api_client = overlord_agent_api.APIClient(API_URL) self.hardware_info = [ hardware.HardwareInfo(hardware.HardwareType.MAC_ADDRESS, - 'a:b:c:d'), + 'aa:bb:cc:dd:ee:ff'), hardware.HardwareInfo(hardware.HardwareType.MAC_ADDRESS, - '0:1:2:3'), + 'ff:ee:dd:cc:bb:aa'), ] def test_successful_heartbeat(self): @@ -47,16 +47,17 @@ class TestBaseTeethAgent(unittest.TestCase): self.api_client.session.request.return_value = response heartbeat_before = self.api_client.heartbeat( - uuid='fake-uuid', - listen_address=('42.42.42.42', '9999') + uuid='deadbeef-dabb-ad00-b105-f00d00bab10c', + advertise_address=('42.42.42.42', '9999') ) self.assertEqual(heartbeat_before, expected_heartbeat_before) + heartbeat_path = 'v1/nodes/deadbeef-dabb-ad00-b105-f00d00bab10c/' \ + 'vendor_passthru/heartbeat' request_args = self.api_client.session.request.call_args[0] self.assertEqual(request_args[0], 'POST') - self.assertEqual(request_args[1], API_URL + 'v1/nodes/fake-uuid/vendor' - '_passthru/heartbeat') + self.assertEqual(request_args[1], API_URL + heartbeat_path) def test_heartbeat_requests_exception(self): self.api_client.session.request = mock.Mock() @@ -64,8 +65,8 @@ class TestBaseTeethAgent(unittest.TestCase): self.assertRaises(errors.HeartbeatError, self.api_client.heartbeat, - uuid='fake-uuid', - listen_address=('42.42.42.42', '9999')) + uuid='deadbeef-dabb-ad00-b105-f00d00bab10c', + advertise_address=('42.42.42.42', '9999')) def test_heartbeat_invalid_status_code(self): response = httmock.response(status_code=404) @@ -74,8 +75,8 @@ class TestBaseTeethAgent(unittest.TestCase): self.assertRaises(errors.HeartbeatError, self.api_client.heartbeat, - uuid='fake-uuid', - listen_address=('42.42.42.42', '9999')) + uuid='deadbeef-dabb-ad00-b105-f00d00bab10c', + advertise_address=('42.42.42.42', '9999')) def test_heartbeat_missing_heartbeat_before_header(self): response = httmock.response(status_code=204) @@ -84,8 +85,8 @@ class TestBaseTeethAgent(unittest.TestCase): self.assertRaises(errors.HeartbeatError, self.api_client.heartbeat, - uuid='fake-uuid', - listen_address=('42.42.42.42', '9999')) + uuid='deadbeef-dabb-ad00-b105-f00d00bab10c', + advertise_address=('42.42.42.42', '9999')) def test_heartbeat_invalid_heartbeat_before_header(self): response = httmock.response(status_code=204, headers={ @@ -96,13 +97,13 @@ class TestBaseTeethAgent(unittest.TestCase): self.assertRaises(errors.HeartbeatError, self.api_client.heartbeat, - uuid='fake-uuid', - listen_address=('42.42.42.42', '9999')) + uuid='deadbeef-dabb-ad00-b105-f00d00bab10c', + advertise_address=('42.42.42.42', '9999')) def test_lookup_node(self): response = httmock.response(status_code=200, content={ 'node': { - 'uuid': 'fake-uuid' + 'uuid': 'deadbeef-dabb-ad00-b105-f00d00bab10c' } }) @@ -122,18 +123,18 @@ class TestBaseTeethAgent(unittest.TestCase): self.assertEqual(content['hardware'], [ { 'type': 'mac_address', - 'id': 'a:b:c:d', + 'id': 'aa:bb:cc:dd:ee:ff', }, { 'type': 'mac_address', - 'id': '0:1:2:3', + 'id': 'ff:ee:dd:cc:bb:aa', }, ]) def test_lookup_node_bad_response_code(self): response = httmock.response(status_code=400, content={ 'node': { - 'uuid': 'fake-uuid' + 'uuid': 'deadbeef-dabb-ad00-b105-f00d00bab10c' } })