Skip to content

Commit

Permalink
Deprecate CONF.fixed_range, do dynamic setup
Browse files Browse the repository at this point in the history
Do dynamic fixed_range setup by pulling the networks that should
exist on the host and making the appropriate calls to set up the NAT
entries for each network. This allows for non-contiguous subnets to
be configured in the fixed_ip space and only configures the NAT rules
as they are needed. This also restricts the NAT range to the smallest
range required preventing the NAT from impacting subnets that might
exist on the external network.

DocImpact: For backwards compatibility, Grizzly will still support
the CONF.fixed_range option and if set will perform the default logic
from Folsom and earlier releases. To use the new dynamic fixed_range
setup in Grizzly, set fixed_range='' in your nova.conf. The intention
is to remove the CONF.fixed_range option entirely early in the Havana
cycle and use the dynamic fixed_range setup from Havana going
forward.

Change-Id: I4ec111079f7a1d253190e6a6008048f992a53f68
Fixes: bug #741626 bug #966175
  • Loading branch information
mathrock committed Mar 12, 2013
1 parent ae251b9 commit 26440ae
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 5 deletions.
10 changes: 8 additions & 2 deletions nova/network/l3.py
Expand Up @@ -79,7 +79,13 @@ def initialize(self, **kwargs):
if self.initialized:
return
LOG.debug("Initializing linux_net L3 driver")
linux_net.init_host()
fixed_range = kwargs.get('fixed_range', False)
networks = kwargs.get('networks', None)
if not fixed_range and networks is not None:
for network in networks:
self.initialize_network(network['cidr'])
else:
linux_net.init_host()
linux_net.ensure_metadata_ip()
linux_net.metadata_forward()
self.initialized = True
Expand All @@ -88,7 +94,7 @@ def is_initialized(self):
return self.initialized

def initialize_network(self, cidr):
linux_net.add_snat_rule(cidr)
linux_net.init_host(cidr)

def initialize_gateway(self, network_ref):
mac_address = utils.generate_mac_address()
Expand Down
23 changes: 20 additions & 3 deletions nova/network/manager.py
Expand Up @@ -106,9 +106,12 @@
cfg.IntOpt('network_size',
default=256,
help='Number of addresses in each private subnet'),
# TODO(mathrock): Deprecate in Grizzly, remove in Havana
cfg.StrOpt('fixed_range',
default='10.0.0.0/8',
help='Fixed IP address block'),
help='DEPRECATED - Fixed IP address block.'
'If set to an empty string, the subnet range(s) will be '
'automatically determined and configured.'),
cfg.StrOpt('fixed_range_v6',
default='fd00::/48',
help='Fixed IPv6 address block'),
Expand Down Expand Up @@ -1559,14 +1562,21 @@ def init_host(self):
"""Do any initialization that needs to be run if this is a
standalone service.
"""
self.l3driver.initialize()
if not CONF.fixed_range:
ctxt = context.get_admin_context()
networks = self.db.network_get_all_by_host(ctxt, self.host)
self.l3driver.initialize(fixed_range=False, networks=networks)
else:
self.l3driver.initialize(fixed_range=CONF.fixed_range)
super(FlatDHCPManager, self).init_host()
self.init_host_floating_ips()

def _setup_network_on_host(self, context, network):
"""Sets up network on this host."""
network['dhcp_server'] = self._get_dhcp_ip(context, network)

if not CONF.fixed_range:
self.l3driver.initialize_network(network.get('cidr'))
self.l3driver.initialize_gateway(network)

if not CONF.fake_network:
Expand Down Expand Up @@ -1630,7 +1640,12 @@ def init_host(self):
standalone service.
"""

self.l3driver.initialize()
if not CONF.fixed_range:
ctxt = context.get_admin_context()
networks = self.db.network_get_all_by_host(ctxt, self.host)
self.l3driver.initialize(fixed_range=False, networks=networks)
else:
self.l3driver.initialize(fixed_range=CONF.fixed_range)
NetworkManager.init_host(self)
self.init_host_floating_ips()

Expand Down Expand Up @@ -1773,6 +1788,8 @@ def _setup_network_on_host(self, context, network):
address = network['vpn_public_address']
network['dhcp_server'] = self._get_dhcp_ip(context, network)

if not CONF.fixed_range:
self.l3driver.initialize_network(network.get('cidr'))
self.l3driver.initialize_gateway(network)

# NOTE(vish): only ensure this forward if the address hasn't been set
Expand Down
201 changes: 201 additions & 0 deletions nova/tests/network/test_manager.py
Expand Up @@ -1601,6 +1601,207 @@ def test_disassociate_network_not_found(self):
self.assertRaises(exception.NetworkNotFound,
manager.disassociate_network, fake_context, uuid)

def _test_init_host_static_fixed_range(self, net_manager):
self.flags(fake_network=True,
fixed_range='10.0.0.0/22',
routing_source_ip='192.168.0.1',
metadata_host='192.168.0.1',
public_interface='eth1',
dmz_cidr=['10.0.3.0/24'])
binary_name = linux_net.get_binary_name()

# Stub out calls we don't want to really run
self.stubs.Set(linux_net.iptables_manager, '_apply', lambda: None)
self.stubs.Set(floating_ips.FloatingIP, 'init_host_floating_ips',
lambda *args: None)

# Call the network manager init code to configure the fixed_range
net_manager.init_host()

# Get the iptables rules that got created
current_lines = []
new_lines = linux_net.iptables_manager._modify_rules(current_lines,
linux_net.iptables_manager.ipv4['nat'],
table_name='nat')

# The expected rules that should be configured based on the fixed_range
expected_lines = ['[0:0] -A %s-snat -s %s -j SNAT --to-source %s -o %s'
% (binary_name, CONF.fixed_range,
CONF.routing_source_ip,
CONF.public_interface),
'[0:0] -A %s-POSTROUTING -s %s -d %s/32 -j ACCEPT'
% (binary_name, CONF.fixed_range,
CONF.metadata_host),
'[0:0] -A %s-POSTROUTING -s %s -d %s -j ACCEPT'
% (binary_name, CONF.fixed_range, CONF.dmz_cidr[0]),
'[0:0] -A %s-POSTROUTING -s %s -d %s -m conntrack ! '
'--ctstate DNAT -j ACCEPT' % (binary_name,
CONF.fixed_range,
CONF.fixed_range)]

# Finally, compare the expected rules against the actual ones
for line in expected_lines:
self.assertTrue(line in new_lines)

def _test_init_host_dynamic_fixed_range(self, net_manager):
self.flags(fake_network=True,
fixed_range='',
routing_source_ip='172.16.0.1',
metadata_host='172.16.0.1',
public_interface='eth1',
dmz_cidr=['10.0.3.0/24'])
binary_name = linux_net.get_binary_name()

# Stub out calls we don't want to really run, mock the db
self.stubs.Set(linux_net.iptables_manager, '_apply', lambda: None)
self.stubs.Set(floating_ips.FloatingIP, 'init_host_floating_ips',
lambda *args: None)
self.stubs.Set(net_manager.l3driver, 'initialize_gateway',
lambda *args: None)
self.mox.StubOutWithMock(db, 'network_get_all_by_host')
db.network_get_all_by_host(mox.IgnoreArg(),
mox.IgnoreArg()).MultipleTimes().AndReturn(networks)
self.mox.ReplayAll()

# Call the network manager init code to configure the fixed_range
net_manager.init_host()

# Get the iptables rules that got created
current_lines = []
new_lines = linux_net.iptables_manager._modify_rules(current_lines,
linux_net.iptables_manager.ipv4['nat'],
table_name='nat')

# The expected rules that should be configured based on the fixed_range
expected_lines = ['[0:0] -A %s-snat -s %s -j SNAT --to-source %s -o %s'
% (binary_name, networks[0]['cidr'],
CONF.routing_source_ip,
CONF.public_interface),
'[0:0] -A %s-POSTROUTING -s %s -d %s/32 -j ACCEPT'
% (binary_name, networks[0]['cidr'],
CONF.metadata_host),
'[0:0] -A %s-POSTROUTING -s %s -d %s -j ACCEPT'
% (binary_name, networks[0]['cidr'],
CONF.dmz_cidr[0]),
'[0:0] -A %s-POSTROUTING -s %s -d %s -m conntrack ! '
'--ctstate DNAT -j ACCEPT' % (binary_name,
networks[0]['cidr'],
networks[0]['cidr']),
'[0:0] -A %s-snat -s %s -j SNAT --to-source %s -o %s'
% (binary_name, networks[1]['cidr'],
CONF.routing_source_ip,
CONF.public_interface),
'[0:0] -A %s-POSTROUTING -s %s -d %s/32 -j ACCEPT'
% (binary_name, networks[1]['cidr'],
CONF.metadata_host),
'[0:0] -A %s-POSTROUTING -s %s -d %s -j ACCEPT'
% (binary_name, networks[1]['cidr'],
CONF.dmz_cidr[0]),
'[0:0] -A %s-POSTROUTING -s %s -d %s -m conntrack ! '
'--ctstate DNAT -j ACCEPT' % (binary_name,
networks[1]['cidr'],
networks[1]['cidr'])]

# Compare the expected rules against the actual ones
for line in expected_lines:
self.assertTrue(line in new_lines)

# Add an additional network and ensure the rules get configured
new_network = {'id': 2,
'uuid': 'cccccccc-cccc-cccc-cccc-cccccccc',
'label': 'test2',
'injected': False,
'multi_host': False,
'cidr': '192.168.2.0/24',
'cidr_v6': '2001:dba::/64',
'gateway_v6': '2001:dba::1',
'netmask_v6': '64',
'netmask': '255.255.255.0',
'bridge': 'fa1',
'bridge_interface': 'fake_fa1',
'gateway': '192.168.2.1',
'broadcast': '192.168.2.255',
'dns1': '192.168.2.1',
'dns2': '192.168.2.2',
'vlan': None,
'host': HOST,
'project_id': 'fake_project',
'vpn_public_address': '192.168.2.2',
'vpn_public_port': '22',
'vpn_private_address': '10.0.0.2'}

# Call the network manager init code to configure the fixed_range
ctxt = context.get_admin_context()
net_manager._setup_network_on_host(ctxt, new_network)

# Get the new iptables rules that got created from adding a new network
current_lines = []
new_lines = linux_net.iptables_manager._modify_rules(current_lines,
linux_net.iptables_manager.ipv4['nat'],
table_name='nat')

# Add the new expected rules to the old ones
expected_lines += ['[0:0] -A %s-snat -s %s -j SNAT --to-source %s -o '
'%s' % (binary_name, new_network['cidr'],
CONF.routing_source_ip,
CONF.public_interface),
'[0:0] -A %s-POSTROUTING -s %s -d %s/32 -j ACCEPT'
% (binary_name, new_network['cidr'],
CONF.metadata_host),
'[0:0] -A %s-POSTROUTING -s %s -d %s -j ACCEPT'
% (binary_name, new_network['cidr'],
CONF.dmz_cidr[0]),
'[0:0] -A %s-POSTROUTING -s %s -d %s -m conntrack '
'! --ctstate DNAT -j ACCEPT' % (binary_name,
new_network['cidr'],
new_network['cidr'])]

# Compare the expected rules (with new network) against the actual ones
for line in expected_lines:
self.assertTrue(line in new_lines)

def test_flatdhcpmanager_static_fixed_range(self):
"""Test FlatDHCPManager NAT rules for fixed_range."""
# Set the network manager
self.network = network_manager.FlatDHCPManager(host=HOST)
self.network.db = db

# Test existing behavior:
# CONF.fixed_range is set, NAT based on CONF.fixed_range
self._test_init_host_static_fixed_range(self.network)

def test_flatdhcpmanager_dynamic_fixed_range(self):
"""Test FlatDHCPManager NAT rules for fixed_range."""
# Set the network manager
self.network = network_manager.FlatDHCPManager(host=HOST)
self.network.db = db

# Test new behavior:
# CONF.fixed_range is not set, defaults to None
# Determine networks to NAT based on lookup
self._test_init_host_dynamic_fixed_range(self.network)

def test_vlanmanager_static_fixed_range(self):
"""Test VlanManager NAT rules for fixed_range."""
# Set the network manager
self.network = network_manager.VlanManager(host=HOST)
self.network.db = db

# Test existing behavior:
# CONF.fixed_range is set, NAT based on CONF.fixed_range
self._test_init_host_static_fixed_range(self.network)

def test_vlanmanager_dynamic_fixed_range(self):
"""Test VlanManager NAT rules for fixed_range."""
# Set the network manager
self.network = network_manager.VlanManager(host=HOST)
self.network.db = db

# Test new behavior:
# CONF.fixed_range is not set, defaults to None
# Determine networks to NAT based on lookup
self._test_init_host_dynamic_fixed_range(self.network)


class TestRPCFixedManager(network_manager.RPCAllocateFixedIP,
network_manager.NetworkManager):
Expand Down

0 comments on commit 26440ae

Please sign in to comment.