Update metadata proxy when subnet add/delete

With current code, if first subnet of the network is an ipv6 subnet,
the metadata proxy will not be spawned. If user then adds ipv4 subnet
with dhcp enabled, the metadata proxy will still not be spawned. As a
result, the metadata service will not be available for the network.

This patch will kill/spawn metadata proxy,  when subnet add/delete.
So, even if the first subnet of the network is not an ipv4 subnet with
dhcp enabled, the metadata proxy can still be spawned if network has
subnets need metadata proxy.

Closes-bug: #1556991

Change-Id: I0b45af8f2b756732f45c13d7e2dbcd30653cc026
This commit is contained in:
Hong Hui Xiao 2016-03-14 13:19:52 +00:00 committed by Hong Hui Xiao
parent 51b34f0729
commit 784864d178
5 changed files with 68 additions and 24 deletions

View File

@ -266,14 +266,10 @@ class DhcpAgent(manager.Manager):
if not network.admin_state_up:
return
enable_metadata = self.dhcp_driver_cls.should_enable_metadata(
self.conf, network)
dhcp_network_enabled = False
for subnet in network.subnets:
if subnet.enable_dhcp:
if self.call_driver('enable', network):
dhcp_network_enabled = True
self.update_isolated_metadata_proxy(network)
self.cache.put(network)
# After enabling dhcp for network, mark all existing
# ports as ready. So that the status of ports which are
@ -281,18 +277,6 @@ class DhcpAgent(manager.Manager):
self.dhcp_ready_ports |= {p.id for p in network.ports}
break
if enable_metadata and dhcp_network_enabled:
for subnet in network.subnets:
if subnet.ip_version == 4 and subnet.enable_dhcp:
self.enable_isolated_metadata_proxy(network)
break
elif (not self.conf.force_metadata and
not self.conf.enable_isolated_metadata):
# In the case that the dhcp agent ran with metadata enabled,
# and dhcp agent now starts with metadata disabled, check and
# delete any metadata_proxy.
self.disable_isolated_metadata_proxy(network)
def disable_dhcp_helper(self, network_id):
"""Disable DHCP for a network known to the agent."""
network = self.cache.get_network_by_id(network_id)
@ -332,6 +316,9 @@ class DhcpAgent(manager.Manager):
elif self.call_driver('restart', network):
self.cache.put(network)
# Update the metadata proxy after the dhcp driver has been updated
self.update_isolated_metadata_proxy(network)
@utils.synchronized('dhcp-agent')
def network_create_end(self, context, payload):
"""Handle the network.create.end notification event."""
@ -418,6 +405,20 @@ class DhcpAgent(manager.Manager):
else:
self.call_driver('reload_allocations', network)
def update_isolated_metadata_proxy(self, network):
"""Spawn or kill metadata proxy.
According to return from driver class, spawn or kill the metadata
proxy process. Spawn an existing metadata proxy or kill a nonexistent
metadata proxy will just silently return.
"""
should_enable_metadata = self.dhcp_driver_cls.should_enable_metadata(
self.conf, network)
if should_enable_metadata:
self.enable_isolated_metadata_proxy(network)
else:
self.disable_isolated_metadata_proxy(network)
def enable_isolated_metadata_proxy(self, network):
# The proxy might work for either a single network

View File

@ -1051,7 +1051,9 @@ class Dnsmasq(DhcpLocalProcess):
with 3rd party backends.
"""
if conf.force_metadata:
return True
# Only ipv4 subnet, with dhcp enabled, will use metadata proxy.
return any(s for s in network.subnets
if s.ip_version == 4 and s.enable_dhcp)
if conf.enable_metadata_network and conf.enable_isolated_metadata:
# check if the network has a metadata subnet
@ -1064,7 +1066,10 @@ class Dnsmasq(DhcpLocalProcess):
return False
isolated_subnets = cls.get_isolated_subnets(network)
return any(isolated_subnets[subnet.id] for subnet in network.subnets)
# Only ipv4 isolated subnet, which has dhcp enabled, will use
# metadata proxy.
return any(isolated_subnets[s.id] for s in network.subnets
if s.ip_version == 4 and s.enable_dhcp)
class DeviceManager(object):

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
import os.path
import eventlet
@ -314,6 +315,45 @@ class DHCPAgentOVSTestCase(DHCPAgentOVSTestFramework):
sleep=0.1,
exception=RuntimeError("Stale metadata proxy didn't get killed"))
def _test_metadata_proxy_spawn_kill_with_subnet_create_delete(self):
network = self.network_dict_for_dhcp(ip_version=6)
self.configure_dhcp_for_network(network=network)
pm = self._get_metadata_proxy_process(network)
# A newly created network with ipv6 subnet will not have metadata proxy
self.assertFalse(pm.active)
new_network = copy.deepcopy(network)
dhcp_enabled_ipv4_subnet = self.create_subnet_dict(network.id)
new_network.subnets.append(dhcp_enabled_ipv4_subnet)
self.mock_plugin_api.get_network_info.return_value = new_network
self.agent.refresh_dhcp_helper(network.id)
# Metadata proxy should be spawned for the newly added subnet
common_utils.wait_until_true(
lambda: pm.active,
timeout=5,
sleep=0.1,
exception=RuntimeError("Metadata proxy didn't spawn"))
self.mock_plugin_api.get_network_info.return_value = network
self.agent.refresh_dhcp_helper(network.id)
# Metadata proxy should be killed because network doesn't need it.
common_utils.wait_until_true(
lambda: not pm.active,
timeout=5,
sleep=0.1,
exception=RuntimeError("Metadata proxy didn't get killed"))
def test_enable_isolated_metadata_for_subnet_create_delete(self):
self.conf.set_override('force_metadata', False)
self.conf.set_override('enable_isolated_metadata', True)
self._test_metadata_proxy_spawn_kill_with_subnet_create_delete()
def test_force_metadata_for_subnet_create_delete(self):
self.conf.set_override('force_metadata', True)
self.conf.set_override('enable_isolated_metadata', False)
self._test_metadata_proxy_spawn_kill_with_subnet_create_delete()
def test_notify_port_ready_after_enable_dhcp(self):
network = self.network_dict_for_dhcp()
dhcp_port = self.create_port_dict(

View File

@ -665,12 +665,10 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
self.external_process.assert_has_calls([
self._process_manager_constructor_call(),
mock.call().enable()])
elif not enable_isolated_metadata:
else:
self.external_process.assert_has_calls([
self._process_manager_constructor_call(ns=None),
mock.call().disable()])
else:
self.assertFalse(self.external_process.call_count)
def test_enable_dhcp_helper_enable_metadata_isolated_network(self):
self._enable_dhcp_helper(isolated_network,

View File

@ -2078,8 +2078,8 @@ class TestDnsmasq(TestBase):
def test_should_force_metadata_returns_true(self):
self.conf.set_override("force_metadata", True)
self.assertTrue(dhcp.Dnsmasq.should_enable_metadata(self.conf,
mock.ANY))
self.assertTrue(dhcp.Dnsmasq.should_enable_metadata(
self.conf, FakeDualNetworkDualDHCP()))
def _test__generate_opts_per_subnet_helper(self, config_opts,
expected_mdt_ip):