Accept new orchestrator config bucket
This adds the ability for the orchestrator to add a new bucket into the config dict keyed 'orchestrator', which can be used to notify the appliance of the specifics about the orchestrator currently managing it. Initially this will be used to inform the appliance where the metadata service is running, but in the future could be extended to do more, specifically around coordination. Change-Id: I4a4009f12ce025d3dc6577d27f877aeb8427b963 Partial-bug: #1524068
This commit is contained in:
parent
01c4e9052d
commit
44610ac1cd
@ -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):
|
||||
|
@ -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' % (
|
||||
|
@ -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 <config>
|
||||
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,
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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', [])
|
||||
|
84
test/unit/drivers/test_metadata.py
Normal file
84
test/unit/drivers/test_metadata.py
Normal file
@ -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)
|
@ -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',
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user