diff --git a/astara_router/defaults.py b/astara_router/defaults.py index c535fd3..2ad2978 100644 --- a/astara_router/defaults.py +++ b/astara_router/defaults.py @@ -39,8 +39,11 @@ METADATA_DEST_ADDRESS = '169.254.169.254' # port for internal network metadata proxy BASE_METADATA_PORT = 9600 -# port for rug metadata service -RUG_META_PORT = 9697 +# default address of orchestrator metadata service +ORCHESTRATOR_METADATA_ADDRESS = 'fdca:3ba5:a17a:acda::1' + +# default port for orchestrator metadata service +ORCHESTRATOR_METADATA_PORT = 9697 def internal_metadata_port(ifname): diff --git a/astara_router/drivers/iptables.py b/astara_router/drivers/iptables.py index ffabbef..f441416 100644 --- a/astara_router/drivers/iptables.py +++ b/astara_router/drivers/iptables.py @@ -196,7 +196,8 @@ class IPTablesManager(base.Manager): # Open SSH, the HTTP API (5000) and the Nova metadata proxy (9697) for port in ( - defaults.SSH, defaults.API_SERVICE, defaults.RUG_META_PORT + defaults.SSH, defaults.API_SERVICE, + defaults.ORCHESTRATOR_METADATA_PORT ): rules.append(Rule( '-A INPUT -i %s -p tcp -m tcp --dport %s -j ACCEPT' % ( diff --git a/astara_router/drivers/metadata.py b/astara_router/drivers/metadata.py index cb298df..b8bf655 100644 --- a/astara_router/drivers/metadata.py +++ b/astara_router/drivers/metadata.py @@ -41,7 +41,7 @@ class MetadataManager(base.Manager): """ super(MetadataManager, self).__init__(root_helper) - def networks_have_changed(self, config): + def should_restart(self, config): """ This function determines if the networks have changed since was initialized. @@ -59,8 +59,14 @@ class MetadataManager(base.Manager): except: # If we can't read the file, assume networks were added/removed return True - config_dict.pop('tenant_id') - return net_ids != set(config_dict.keys()) + + orchestrator_addr = config_dict.get('orchestrator_metadata_address') + orchestrator_port = config_dict.get('orchestrator_metadata_port') + + return ( + net_ids != set(config_dict.get('networks', {}).keys()) or + orchestrator_addr != config.metadata_address or + orchestrator_port != config.metadata_port) def save_config(self, config): """ @@ -108,7 +114,7 @@ def build_config(config): :param config: :rtype: astara_router.models.Configuration """ - config_data = {} + network_data = {} for net in config.networks: if not net.is_tenant_network: @@ -119,10 +125,14 @@ def build_config(config): for ip in a.ip_addresses: ip_instance_map[ip] = a.device_id - config_data[net.id] = { + network_data[net.id] = { 'listen_port': internal_metadata_port(net.interface.ifname), 'ip_instance_map': ip_instance_map } - config_data['tenant_id'] = config.tenant_id - return config_data + return { + 'tenant_id': config.tenant_id, + 'orchestrator_metadata_address': config.metadata_address, + 'orchestrator_metadata_port': config.metadata_port, + 'networks': network_data, + } diff --git a/astara_router/manager.py b/astara_router/manager.py index 786fe94..ade202b 100644 --- a/astara_router/manager.py +++ b/astara_router/manager.py @@ -97,7 +97,7 @@ class RouterManager(ServiceManagerBase): def update_metadata(self): mgr = metadata.MetadataManager() - should_restart = mgr.networks_have_changed(self._config) + should_restart = mgr.should_restart(self._config) mgr.save_config(self._config) if should_restart: mgr.restart() diff --git a/astara_router/metadata_proxy.py b/astara_router/metadata_proxy.py index 7dc1e73..b70f958 100644 --- a/astara_router/metadata_proxy.py +++ b/astara_router/metadata_proxy.py @@ -31,9 +31,6 @@ import requests from werkzeug import exceptions from werkzeug import wrappers -from astara_router import defaults -from astara_router.drivers import ip - LOG = logging.getLogger(__name__) @@ -48,8 +45,18 @@ class NetworkMetadataProxyHandler(object): self.network_id = network_id self.config_file = config_file self.config_mtime = 0 + self._config_dict = {} self._ip_instance_map = {} + @property + def config_dict(self): + config_mtime = os.stat(self.config_file).st_mtime + if config_mtime > self.config_mtime: + LOG.debug("Metadata proxy configuration has changed; reloading...") + self._config_dict = json.load(open(self.config_file)) + self.config_mtime = config_mtime + return self._config_dict + def __call__(self, environ, start_response): request = wrappers.Request(environ) @@ -68,16 +75,16 @@ class NetworkMetadataProxyHandler(object): @property def ip_instance_map(self): - config_mtime = os.stat(self.config_file).st_mtime - if config_mtime > self.config_mtime: - LOG.debug("Metadata proxy configuration has changed; reloading...") - config_dict = json.load(open(self.config_file)) - self._ip_instance_map = config_dict[ - self.network_id - ]['ip_instance_map'] - self.config_mtime = config_mtime + self._ip_instance_map = self.config_dict['networks'][ + self.network_id]['ip_instance_map'] return self._ip_instance_map + @property + def orchestrator_loc(self): + addr = self.config_dict['orchestrator_metadata_address'] + port = self.config_dict['orchestrator_metadata_port'] + return '[%s]:%d' % (addr, port) + def _proxy_request(self, remote_address, path_info, query_string): headers = { 'X-Forwarded-For': remote_address, @@ -88,7 +95,7 @@ class NetworkMetadataProxyHandler(object): url = urlparse.urlunsplit(( 'http', - '[%s]:%d' % (ip.get_rug_address(), defaults.RUG_META_PORT), + self.orchestrator_loc, path_info, query_string, '')) @@ -172,7 +179,7 @@ def main(): pool = eventlet.GreenPool(1000) tenant_id = config_dict.pop('tenant_id') - for network_id, config in config_dict.items(): + for network_id, config in config_dict['networks'].items(): app = NetworkMetadataProxyHandler(tenant_id, network_id, args.config_file) diff --git a/astara_router/models.py b/astara_router/models.py index dbe5319..8140129 100644 --- a/astara_router/models.py +++ b/astara_router/models.py @@ -20,6 +20,8 @@ import re import netaddr +from astara_router import defaults + GROUP_NAME_LENGTH = 15 DEFAULT_AS = 64512 @@ -715,6 +717,12 @@ class RouterConfiguration(SystemConfiguration): Label(name, cidr) for name, cidr in conf_dict.get('labels', {}).iteritems()] + orchestrator_conf = conf_dict.get('orchestrator', {}) + self.metadata_address = orchestrator_conf.get( + 'address', defaults.ORCHESTRATOR_METADATA_ADDRESS) + self.metadata_port = orchestrator_conf.get( + 'metadata_port', defaults.ORCHESTRATOR_METADATA_PORT) + self.floating_ips = [ FloatingIP.from_dict(fip) for fip in conf_dict.get('floating_ips', []) diff --git a/test/unit/drivers/test_metadata.py b/test/unit/drivers/test_metadata.py new file mode 100644 index 0000000..56490cb --- /dev/null +++ b/test/unit/drivers/test_metadata.py @@ -0,0 +1,84 @@ +# Copyright 2014 DreamHost, LLC +# +# Author: DreamHost, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import __builtin__ + +import json +import mock + +from unittest2 import TestCase + +from astara_router.drivers import metadata + +CONFIG = mock.Mock() +CONFIG.hostname = 'astara' +CONFIG.management_address = 'fdca:3ba5:a17a:acda:f816:3eff:fe66:33b6' + + +class HostnameTestCase(TestCase): + """ + """ + def setUp(self): + self.mgr = metadata.MetadataManager() + self.config_dict = { + 'networks': {'tenant_net_id': [], 'public_net_id': []}, + 'orchestrator_metadata_address': '10.0.0.1', + 'orchestrator_metadata_port': '5000', + } + + tenant_net = mock.Mock( + is_tenant_network=mock.Mock(return_value=True), + id='tenant_net_id', + ) + public_net = mock.Mock( + is_tenant_network=mock.Mock(return_value=False), + id='public_net_id', + ) + + self.config = mock.Mock() + self.config.networks = [tenant_net, public_net] + self.config.metadata_address = '10.0.0.1' + self.config.metadata_port = '5000' + + def _test_should_restart(self, exp_result): + config_json = json.dumps(self.config_dict) + with mock.patch.object( + __builtin__, 'open', mock.mock_open(read_data=config_json)): + self.assertEqual( + self.mgr.should_restart(self.config), exp_result) + + def test_should_restart_false(self): + self._test_should_restart(False) + + def test_should_restart_true_networks_change(self): + self.config_dict['networks'] = { + 'foo_net_id': [], 'public_net_id': []} + self._test_should_restart(True) + + def test_should_restart_true_metadata_addr_change(self): + self.config_dict['orchestrator_metadata_address'] = '11.1.1.1' + self._test_should_restart(True) + + def test_should_restart_true_metadata_port_change(self): + self.config_dict['orchestrator_metadata_port'] = '6000' + self._test_should_restart(True) + + def test_should_restart_true_config_read_err(self): + with mock.patch.object( + __builtin__, 'open', mock.mock_open()) as _o: + _o.side_effect = IOError() + self.assertEqual( + self.mgr.should_restart(self.config), True) diff --git a/test/unit/test_metadata_proxy.py b/test/unit/test_metadata_proxy.py index ca76f92..770233b 100644 --- a/test/unit/test_metadata_proxy.py +++ b/test/unit/test_metadata_proxy.py @@ -11,8 +11,14 @@ from astara_router import metadata_proxy config = json.dumps({ "tenant_id": "ABC123", - "net1": {"listen_port": 9602, 'ip_instance_map': {'10.10.10.2': 'VM1'}}, - "net2": {"listen_port": 9603, 'ip_instance_map': {'10.10.10.2': 'VM2'}}, + "orchestrator_metadata_address": "192.168.25.30", + "orchestrator_metadata_port": 9697, + "networks": { + "net1": { + "listen_port": 9602, 'ip_instance_map': {'10.10.10.2': 'VM1'}}, + "net2": { + "listen_port": 9603, 'ip_instance_map': {'10.10.10.2': 'VM2'}}, + } }) class TestMetadataProxy(unittest.TestCase): @@ -66,7 +72,7 @@ class TestMetadataProxy(unittest.TestCase): get.return_value.status_code = 200 wsgi._proxy_request('10.10.10.2', '/', '') get.assert_called_once_with( - 'http://[fdca:3ba5:a17a:acda::1]:9697/', + 'http://[192.168.25.30]:9697/', headers={ 'X-Quantum-Network-ID': 'net1', 'X-Forwarded-For': '10.10.10.2', diff --git a/test/unit/test_models.py b/test/unit/test_models.py index 473ff56..25a33b9 100644 --- a/test/unit/test_models.py +++ b/test/unit/test_models.py @@ -23,7 +23,7 @@ import netaddr from unittest2 import TestCase -from astara_router import models +from astara_router import defaults, models from test.unit import fakes @@ -439,6 +439,23 @@ class RouterConfigurationTestCase(TestCase): self.assertEqual(len(c.anchors[0].rules), 1) self.assertEqual(c.anchors[0].rules[0].action, 'block') + def test_init_metadata_config(self): + c = models.RouterConfiguration({ + 'orchestrator': { + 'address': '192.168.25.30', + 'metadata_port': 9697, + } + }) + self.assertEqual(c.metadata_address, '192.168.25.30') + self.assertEqual(c.metadata_port, 9697) + + def test_init_metadata_config_missing(self): + c = models.RouterConfiguration({}) + self.assertEqual( + c.metadata_address, defaults.ORCHESTRATOR_METADATA_ADDRESS) + self.assertEqual( + c.metadata_port, defaults.ORCHESTRATOR_METADATA_PORT) + def test_asn_default(self): c = models.RouterConfiguration({'networks': []}) self.assertEqual(c.asn, 64512)